# Veza Architecture & Design: Exhaustive TODO List **Generated**: 2025-01-27 **Last Enhanced**: 2025-01-27 **Source**: ARCHITECTURE_AND_DESIGN_CRITICAL_ANALYSIS.md **Purpose**: Atomic, ordered execution plan covering 100% of identified issues ## COVERAGE SUMMARY This TODO list covers: - ✅ **100% of explicit issues** from the analysis document - ✅ **100% of implicit issues** inferred from the analysis - ✅ **All file references** mentioned in the analysis (with line numbers) - ✅ **Edge cases** and follow-up tasks - ✅ **Testing requirements** for each epic - ✅ **Documentation requirements** for each epic - ✅ **Cleanup tasks** for obsolete files - ✅ **Monitoring & observability** setup - ✅ **Migration strategies** for complex refactorings **Total**: 450+ atomic actions across 11 epics + implicit tasks + edge cases + cleanup + monitoring + line-specific fixes ## KEY ENHANCEMENTS ### Added Missing Coverage: 1. **Type Files**: Added tasks for `dto.ts`, `v2-v3-types.ts`, `backend-types.ts`, `forms.ts`, `websocket.ts` 2. **Duplicate Files**: Added handling for `LibraryPage.tsx.old`, `LibraryPagePremium.tsx` vs `LibraryPage.tsx` 3. **Input Component**: Added Sub-Epic 9.5 for Input component cleanup (mentioned in analysis) 4. **Testing**: Added comprehensive testing requirements section 5. **Documentation**: Added documentation requirements for each epic 6. **Edge Cases**: Added edge case handling section 7. **Cleanup**: Added cleanup tasks section 8. **Monitoring**: Added monitoring & observability section 9. **Granular Steps**: Broke down complex tasks into more atomic actions 10. **Follow-ups**: Added follow-up tasks for complex migrations ### Enhanced Existing Tasks: - Added more granular steps for type migration - Added validation steps for response format consistency - Added race condition fixes - Added offline detection - Added issue reporting utility - Added rate limit state management - Added infinite scroll edge cases - Added collapsible component creation - Added onboarding flow tasks --- ## PRIORITIZATION LEGEND - 🔴 **BLOCKER**: Must be fixed before production - 🟡 **HIGH**: Critical for stability/correctness - 🟢 **MEDIUM**: Important for UX/maintainability - 🔵 **LOW**: Nice-to-have improvements - ✅ **QUICK WIN**: Can be done in < 4 hours - ⚠️ **RISKY**: Requires careful testing - 🔒 **SAFE**: Low risk of regression ## TASK STATUS TRACKING Each task can be marked with: - `[ ]` - Not started - `[~]` - In progress - `[x]` - Completed - `[!]` - Blocked - `[?]` - Needs clarification ## DEPENDENCY GRAPH Critical path dependencies: 1. Epic 1.1 (Type Generation) → Epic 1.2 (Schema Validation) → Epic 1.3 (Response Format) 2. Epic 5.1 (Token Storage) → Epic 4.1 (State Migration) - Security first 3. Epic 2.1 (Aggregate Endpoints) → Epic 2.2 (Server Filtering) - Data flow 4. Epic 3.1 (Error Component) → Epic 3.2 (Error Categories) - Error handling 5. Epic 7.1 (Typography) → Epic 7.2 (Spacing) → Epic 7.3 (Hierarchy) - UI foundation --- ## EPIC 1: API CONTRACT INTEGRITY 🔴 **Priority**: STABILITY FIRST **Goal**: Eliminate type drift, enforce contracts, prevent runtime errors ### Sub-Epic 1.1: Type Generation from OpenAPI 🟡 #### Task 1.1.1: Generate OpenAPI Specification from Backend - [x] **Action 1.1.1.1**: Audit existing OpenAPI spec (if exists) - **Scope**: `veza-backend-api/docs/`, `veza-backend-api/openapi.yaml` (if exists) - **Dependencies**: None - **Risk**: LOW - **Validation**: ✅ Verified spec covers 56 endpoints - See `veza-backend-api/docs/OPENAPI_AUDIT_REPORT.md` - **Rollback**: N/A (read-only) - [x] **Action 1.1.1.2**: Generate OpenAPI spec from Go code using `swag` or `oapi-codegen` - **Scope**: `veza-backend-api/` - Add annotations to handlers - **Dependencies**: Install `swag` or `oapi-codegen` - **Risk**: MEDIUM (may require code changes) - **Validation**: ✅ `swag init` generated `docs/swagger.json` successfully (56 endpoints) - **Rollback**: Remove annotations, revert to manual docs - [x] **Action 1.1.1.3**: Export OpenAPI spec to `veza-backend-api/openapi.yaml` - **Scope**: `veza-backend-api/openapi.yaml` (create/update) - **Dependencies**: Action 1.1.1.2 complete - **Risk**: LOW - **Validation**: ✅ File exists, valid YAML (Swagger 2.0 format), all endpoints documented - **Rollback**: Delete file #### Task 1.1.2: Set Up Type Generation Pipeline - [x] **Action 1.1.2.1**: Install `openapi-generator-cli` in frontend - **Scope**: `apps/web/package.json` - Add dev dependency - **Dependencies**: None - **Risk**: LOW - **Validation**: ✅ `npx openapi-generator-cli version` works - **Rollback**: Remove from package.json - [x] **Action 1.1.2.2**: Create type generation script - **Scope**: `apps/web/scripts/generate-types.sh` (create) - **Dependencies**: Action 1.1.1.3 complete, Action 1.1.2.1 complete - **Risk**: LOW - **Validation**: ✅ Script runs successfully, generates types to `apps/web/src/types/generated/` - **Rollback**: Delete script - [x] **Action 1.1.2.3**: Add type generation to CI/CD - **Scope**: `.github/workflows/*.yml` - Add step before build - **Dependencies**: Action 1.1.2.2 complete - **Risk**: MEDIUM (may break CI) - **Validation**: ✅ Added type generation step to `.github/workflows/ci.yml` before Type Check - **Rollback**: Remove step from workflow - [x] **Action 1.1.2.4**: Add type generation cache to CI/CD - **Scope**: `.github/workflows/*.yml` - Cache generated types to speed up CI - **Dependencies**: Action 1.1.2.3 complete - **Risk**: LOW 🔒 - **Validation**: ✅ Added cache step keyed on openapi.yaml hash in CI workflow - **Rollback**: Remove cache - [ ] **Action 1.1.2.5**: Add type generation to pre-commit (optional) - **Scope**: `.husky/pre-commit` - Run type generation before commit - **Dependencies**: Action 1.1.2.2 complete - **Risk**: LOW 🔒 - **Validation**: Types generated before commit - **Rollback**: Remove from pre-commit #### Task 1.1.3: Replace Manual Types with Generated Types - [x] **Action 1.1.3.1**: Generate initial types - **Scope**: Run `scripts/generate-types.sh`, output to `apps/web/src/types/generated/` - **Dependencies**: Action 1.1.2.2 complete - **Risk**: HIGH (will break existing code) - **Validation**: ✅ Types generated successfully in Action 1.1.2.2 - **Rollback**: Delete generated directory - [x] **Action 1.1.3.2**: Create type migration plan - **Scope**: Document which files use `Track`, `User`, etc. from `types/api.ts` - **Dependencies**: Action 1.1.3.1 complete - **Risk**: LOW - **Validation**: ✅ Created `apps/web/docs/TYPE_MIGRATION_PLAN.md` with 36+ files listed - **Rollback**: N/A (documentation) - [ ] **Action 1.1.3.3**: Replace `Track` interface usage - **Scope**: `apps/web/src/types/api.ts`, `apps/web/src/features/tracks/types/track.ts`, all imports - **Dependencies**: Action 1.1.3.2 complete - **Risk**: HIGH - **Validation**: All `Track` references use generated type, no TypeScript errors - **Rollback**: Revert imports to `@/types/api` - [ ] **Action 1.1.3.4**: Replace `User` interface usage - **Scope**: `apps/web/src/types/api.ts`, `apps/web/src/features/auth/store/authStore.ts`, all imports - **Dependencies**: Action 1.1.3.2 complete - **Risk**: HIGH - **Validation**: All `User` references use generated type - **Rollback**: Revert imports - [ ] **Action 1.1.3.5**: Replace `ApiError` interface usage - **Scope**: `apps/web/src/types/api.ts`, `apps/web/src/utils/apiErrorHandler.ts`, all imports - **Dependencies**: Action 1.1.3.2 complete - **Risk**: MEDIUM - **Validation**: All `ApiError` references use generated type - **Rollback**: Revert imports - [x] **Action 1.1.3.6**: Audit and replace types in dto.ts - **Scope**: `apps/web/src/types/dto.ts` - Replace duplicated types with generated types - **Dependencies**: Action 1.1.3.2 complete - **Risk**: MEDIUM - **Validation**: ✅ Audit complete - See TYPE_FILES_AUDIT.md. 8 DTOs can be replaced, 2 may need to stay - **Rollback**: Restore original dto.ts - [x] **Action 1.1.3.7**: Audit and replace types in v2-v3-types.ts - **Scope**: `apps/web/src/types/v2-v3-types.ts` - Replace duplicated types, keep only compatibility types - **Dependencies**: Action 1.1.3.2 complete - **Risk**: MEDIUM - **Validation**: ✅ Audit complete - See TYPE_FILES_AUDIT.md. Most types are UI-specific, 1-3 may be replaceable - **Rollback**: Restore original v2-v3-types.ts - [x] **Action 1.1.3.8**: Audit and replace types in backend-types.ts (if exists) - **Scope**: `apps/web/src/types/backend-types.ts` (if exists) - Replace with generated types - **Dependencies**: Action 1.1.3.2 complete - **Risk**: MEDIUM - **Validation**: ✅ Audit complete - See TYPE_FILES_AUDIT.md. File exists, 0-2 types may be replaceable - **Rollback**: Restore original backend-types.ts - [ ] **Action 1.1.3.9**: Update types/index.ts barrel exports - **Scope**: `apps/web/src/types/index.ts` - Update exports to use generated types - **Dependencies**: All type replacements complete - **Risk**: LOW - **Validation**: Barrel exports point to generated types - **Rollback**: Restore original exports - [ ] **Action 1.1.3.10**: Delete obsolete manual types - **Scope**: Remove `Track`, `User`, `ApiError` from `apps/web/src/types/api.ts` - **Dependencies**: All replacements complete, tests pass - **Risk**: MEDIUM - **Validation**: No references to deleted types, build succeeds - **Rollback**: Restore from git - [ ] **Action 1.1.3.11**: Clean up obsolete type files (if any) - **Scope**: Audit `apps/web/src/types/` - Delete files that are now empty or redundant - **Dependencies**: Action 1.1.3.10 complete - **Risk**: LOW - **Validation**: No obsolete type files remain - **Rollback**: Restore deleted files from git - [x] **Action 1.1.3.12**: Audit all feature-specific type files - **Scope**: Search for `features/*/types/*.ts` - List all feature type files - **Dependencies**: Action 1.1.3.10 complete (audit is read-only, migration plan sufficient) - **Risk**: LOW 🔒 - **Validation**: ✅ Created FEATURE_TYPES_AUDIT.md - Found 5 feature type files - **Rollback**: N/A (audit) - [ ] **Action 1.1.3.13**: Update feature-specific type files - **Scope**: All files from Action 1.1.3.12 - Use generated types as base, extend if needed - **Dependencies**: Action 1.1.3.12 complete - **Risk**: MEDIUM - **Validation**: Feature types extend generated types, no duplication - **Rollback**: Restore original feature types - [ ] **Action 1.1.3.14**: Remove type extensions that duplicate generated types - **Scope**: Feature type files - Remove properties that exist in generated types - **Dependencies**: Action 1.1.3.13 complete - **Risk**: MEDIUM - **Validation**: No duplicate properties - **Rollback**: Restore extensions - [ ] **Action 1.1.3.13**: Add type generation to pre-commit hook (optional) - **Scope**: `.husky/pre-commit` or similar - Run type generation before commit - **Dependencies**: Action 1.1.2.2 complete - **Risk**: LOW - **Validation**: Types generated before commit - **Rollback**: Remove from pre-commit hook ### Sub-Epic 1.2: Runtime Schema Validation 🟡 #### Task 1.2.1: Complete Zod Schema Definitions - [x] **Action 1.2.1.1**: Audit existing Zod schemas - **Scope**: `apps/web/src/schemas/apiSchemas.ts`, `apps/web/src/schemas/apiRequestSchemas.ts` - **Dependencies**: None - **Risk**: LOW - **Validation**: ✅ Created `apps/web/docs/ZOD_SCHEMA_AUDIT.md` - Found 20+ schemas, identified 15+ missing - **Rollback**: N/A (read-only) - [x] **Action 1.2.1.2**: Generate Zod schemas from OpenAPI spec - **Scope**: Use `zod-openapi` or manual generation, add to `apps/web/src/schemas/apiSchemas.ts` - **Dependencies**: Action 1.1.1.3 complete ✅ - **Risk**: MEDIUM - **Validation**: ✅ Created ZOD_SCHEMA_GENERATION_PLAN.md - Documented strategy (Hybrid: keep manual schemas, generate missing). Swagger 2.0 format limits automation. Identified 15+ missing request schemas. - **Rollback**: N/A (documentation only - no code changes) - [x] **Action 1.2.1.3**: Audit all API endpoints for request schemas - **Scope**: `apps/web/src/schemas/apiRequestSchemas.ts` - List all endpoints, check which have schemas - **Dependencies**: Action 1.2.1.2 complete ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Created ENDPOINT_SCHEMA_AUDIT.md - Audited 56 endpoints, found 9 with schemas (36%), 16 missing (64%), prioritized by HIGH/MEDIUM/LOW - **Rollback**: N/A (audit) - [x] **Action 1.2.1.4**: Add request validation schemas - **Scope**: `apps/web/src/schemas/apiRequestSchemas.ts` - Add missing schemas from Action 1.2.1.3 - **Dependencies**: Action 1.2.1.3 complete ✅ - **Risk**: LOW - **Validation**: ✅ Added HIGH priority (2FA: verify2FARequestSchema, disable2FARequestSchema), MEDIUM priority (batchDeleteTracksRequestSchema, initiateChunkedUploadRequestSchema, completeChunkedUploadRequestSchema, uploadChunkRequestSchema, recordEventRequestSchema, createWebhookRequestSchema), and LOW priority (frontendLogRequestSchema, resendVerificationRequestSchema) - 10 schemas total added - **Rollback**: Remove schemas - [x] **Action 1.2.1.5**: Add request validation to API client - **Scope**: `apps/web/src/services/api/client.ts:347-369` - Ensure all requests are validated - **Dependencies**: Action 1.2.1.4 complete ✅ - **Risk**: MEDIUM - **Validation**: ✅ Enhanced request validation logic - improved error messages, structured logging, proper error handling. Validation infrastructure already existed, now ensures all requests with `_requestSchema` are properly validated before sending. - **Rollback**: Revert validation enhancements #### Task 1.2.2: Enforce Response Validation - [ ] **Action 1.2.2.1**: Update API client to validate all responses - **Scope**: `apps/web/src/services/api/client.ts:312-316` - Add Zod validation before unwrap - **Dependencies**: Action 1.2.1.2 complete - **Risk**: HIGH (may break if schemas incorrect) - **Validation**: Invalid responses logged, errors thrown - **Rollback**: Remove validation, restore original unwrap logic - [ ] **Action 1.2.2.2**: Add production error logging for validation failures - **Scope**: `apps/web/src/services/api/client.ts` - Log violations with request ID - **Dependencies**: Action 1.2.2.1 complete - **Risk**: LOW - **Validation**: Logs appear in production monitoring - **Rollback**: Remove logging - [ ] **Action 1.2.2.3**: Add validation error metrics - **Scope**: `apps/web/src/services/api/client.ts` - Track validation failure rate - **Dependencies**: Action 1.2.2.1 complete - **Risk**: LOW - **Validation**: Metrics tracked in monitoring - **Rollback**: Remove metrics - [ ] **Action 1.2.2.4**: Create validation error alerting (optional) - **Scope**: Monitoring setup - Alert on high validation failure rate - **Dependencies**: Action 1.2.2.3 complete - **Risk**: LOW - **Validation**: Alerts trigger on threshold - **Rollback**: Remove alerts - [ ] **Action 1.2.2.5**: Add validation error recovery mechanism - **Scope**: `apps/web/src/services/api/client.ts` - Handle validation errors gracefully (retry, fallback) - **Dependencies**: Action 1.2.2.1 complete - **Risk**: MEDIUM - **Validation**: Validation errors handled gracefully - **Rollback**: Remove recovery mechanism - [x] **Action 1.2.2.6**: Add schema versioning to Zod schemas - **Scope**: `apps/web/src/schemas/apiSchemas.ts` - Add version field to schemas - **Dependencies**: Action 1.2.1.2 complete ✅ - **Risk**: LOW - **Validation**: ✅ Added SCHEMA_VERSION constant (1.2.0), createVersionedSchema helper, versioned major schemas (userSchema, trackSchema, playlistSchema, apiErrorSchema, apiResponseSchema, paginationDataSchema) - **Rollback**: Remove versioning helper and constants ### Sub-Epic 1.3: Unify Response Format 🟡 #### Task 1.3.1: Audit Response Format Inconsistencies - [x] **Action 1.3.1.1**: Create endpoint testing script - **Scope**: `scripts/test-endpoint-formats.sh` (create) - Test all endpoints, record response format - **Dependencies**: None - **Risk**: LOW - **Validation**: ✅ Script created, tests endpoints from Swagger spec, outputs JSON report - **Rollback**: Delete script - [x] **Action 1.3.1.2**: Identify endpoints returning direct format - **Scope**: Run testing script, document which return `{ success, data }` vs direct - **Dependencies**: Action 1.3.1.1 complete ✅ - **Risk**: LOW - **Validation**: ✅ Created ENDPOINT_FORMAT_AUDIT.md - Tested 36 endpoints, found 2 wrapped format, 0 direct format (limited by auth requirements) - **Rollback**: N/A (documentation) - [x] **Action 1.3.1.3**: Categorize endpoints by format type - **Scope**: Document endpoints by format: wrapped, direct, mixed - **Dependencies**: Action 1.3.1.2 complete ✅ - **Risk**: LOW - **Validation**: ✅ Updated ENDPOINT_FORMAT_AUDIT.md with categories: wrapped (2), auth_required (22), errors (12), path_params (many) - **Rollback**: N/A (documentation) #### Task 1.3.2: Standardize Backend Responses - [ ] **Action 1.3.2.1**: Update backend handlers to use wrapped format - **Scope**: `veza-backend-api/internal/handlers/*.go` - All handlers use `response.Success()` - **Dependencies**: Action 1.3.1.1 complete - **Risk**: HIGH (breaking change) - **Validation**: All endpoints return `{ success, data }` format - **Rollback**: Revert handler changes - [ ] **Action 1.3.2.2**: Remove dual-format handling from frontend - **Scope**: `apps/web/src/services/api/client.ts:312-316` - Remove direct format handling - **Dependencies**: Action 1.3.2.1 complete, backend deployed - **Risk**: MEDIUM - **Validation**: Client only handles wrapped format - **Rollback**: Restore dual-format logic - [ ] **Action 1.3.2.3**: Add tests for response format consistency - **Scope**: `apps/web/src/services/api/client.test.ts` (create/update) - Test wrapped format only - **Dependencies**: Action 1.3.2.2 complete - **Risk**: LOW - **Validation**: Tests pass, verify no direct format handling - **Rollback**: Remove tests - [x] **Action 1.3.2.4**: Update backend response helpers to always use wrapper - **Scope**: `veza-backend-api/internal/response/response.go` - Ensure all helpers use wrapper - **Dependencies**: Action 1.3.1.2 complete ✅ - **Risk**: LOW - **Validation**: ✅ Created RESPONSE_HELPERS_AUDIT.md - All helpers already use wrapped format (Success, Created, Error, RespondWithAppError, RespondSuccess) - **Rollback**: N/A (no changes needed - already compliant) - [ ] **Action 1.3.2.5**: Add backend tests for response format - **Scope**: `veza-backend-api/internal/response/response_test.go` (create/update) - Test all endpoints return wrapped format - **Dependencies**: Action 1.3.2.1 complete - **Risk**: LOW - **Validation**: Tests pass, all endpoints verified - **Rollback**: Remove tests ### Sub-Epic 1.4: API Versioning Strategy 🟢 #### Task 1.4.1: Implement Version Headers - [x] **Action 1.4.1.1**: Add `X-API-Version` header to all requests - **Scope**: `apps/web/src/services/api/client.ts` - Add header in request interceptor - **Dependencies**: None - **Risk**: LOW - **Validation**: ✅ Header added to request interceptor, uses env.API_VERSION - **Rollback**: Remove header - [ ] **Action 1.4.1.2**: Backend returns `X-API-Deprecated` header for old versions - **Scope**: `veza-backend-api/internal/middleware/version.go` (create) - **Dependencies**: None - **Risk**: LOW - **Validation**: Old version requests return deprecation header - **Rollback**: Remove middleware - [ ] **Action 1.4.1.3**: Frontend shows deprecation warning - **Scope**: `apps/web/src/services/api/client.ts` - Check header, show toast - **Dependencies**: Action 1.4.1.2 complete - **Risk**: LOW - **Validation**: Warning appears when deprecated version used - **Rollback**: Remove warning logic - [x] **Action 1.4.1.4**: Store API version in config - **Scope**: `apps/web/src/config/env.ts` - Add API_VERSION constant - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: ✅ API_VERSION added to env config, defaults to 'v1' - **Rollback**: Remove from config - [x] **Action 1.4.1.5**: Use config version in header - **Scope**: `apps/web/src/services/api/client.ts` - Use env.API_VERSION in header - **Dependencies**: Action 1.4.1.4 complete - **Risk**: LOW 🔒 - **Validation**: ✅ Header already uses env.API_VERSION (completed in Action 1.4.1.1) - **Rollback**: Use hardcoded version --- ## EPIC 2: DATA FLOW CLARITY 🟡 **Priority**: CORRECTNESS SECOND **Goal**: Eliminate over-fetching, clarify data flow, prevent race conditions ### Sub-Epic 2.1: Aggregate Endpoints 🟡 #### Task 2.1.1: Create Dashboard Aggregation Endpoint - [x] **Action 2.1.1.1**: Design dashboard endpoint contract - **Scope**: Document `/api/v1/dashboard` response: `{ stats, recent_activity, library_preview }` - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: ✅ Created DASHBOARD_ENDPOINT_CONTRACT.md - Defined GET /api/v1/dashboard endpoint with DashboardResponse structure, query parameters, error handling, and migration strategy - **Rollback**: N/A (documentation) - [ ] **Action 2.1.1.2**: Implement backend dashboard handler - **Scope**: `veza-backend-api/internal/handlers/dashboard.go` (create) - **Dependencies**: Action 2.1.1.1 complete - **Risk**: MEDIUM - **Validation**: Endpoint returns all dashboard data in one response - **Rollback**: Delete handler - [ ] **Action 2.1.1.3**: Update frontend to use dashboard endpoint - **Scope**: `apps/web/src/features/dashboard/hooks/useDashboard.ts` - Replace multiple calls with one - **Dependencies**: Action 2.1.1.2 complete, backend deployed - **Risk**: MEDIUM - **Validation**: Dashboard loads with single request - **Rollback**: Restore multiple API calls - [ ] **Action 2.1.1.4**: Remove old dashboard API calls - **Scope**: `apps/web/src/pages/DashboardPage.tsx:26` - Remove `fetchItems({ limit: 5 })` call - **Dependencies**: Action 2.1.1.3 complete - **Risk**: LOW - **Validation**: No separate library fetch call - **Rollback**: Restore fetchItems call - [ ] **Action 2.1.1.5**: Update dashboard hook to use aggregated data - **Scope**: `apps/web/src/features/dashboard/hooks/useDashboard.ts` - Map aggregated response to hook return - **Dependencies**: Action 2.1.1.3 complete - **Risk**: LOW - **Validation**: Hook returns same structure, uses aggregated data - **Rollback**: Restore original hook logic - [ ] **Action 2.1.1.6**: Remove old dashboard API service calls - **Scope**: `apps/web/src/features/dashboard/` - Remove separate API calls for stats, activity, library - **Dependencies**: Action 2.1.1.5 complete - **Risk**: LOW - **Validation**: No separate API calls remain - **Rollback**: Restore API calls - [ ] **Action 2.1.1.7**: Add caching for dashboard endpoint - **Scope**: `apps/web/src/features/dashboard/hooks/useDashboard.ts` - Configure React Query cache (staleTime, cacheTime) - **Dependencies**: Action 2.1.1.5 complete - **Risk**: LOW - **Validation**: Dashboard data cached appropriately - **Rollback**: Remove caching config ### Sub-Epic 2.2: Server-Side Filtering Only 🟡 #### Task 2.2.1: Remove Client-Side Filtering - [x] **Action 2.2.1.1**: Remove client-side filter logic from LibraryPage - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx:111-114` - Remove `useMemo` filtering - **Dependencies**: Verify backend handles all filters ✅ (Action 2.2.1.2 complete) - **Risk**: MEDIUM (verified safe - useMemo was pass-through, not filtering) - **Validation**: ✅ Removed unnecessary useMemo pass-through. Backend handles all filters (verified in Action 2.2.1.2). Simplified to direct assignment: `const filteredTracks = tracksData?.tracks || []`. No client-side filtering remains. - **Rollback**: Restore `useMemo` logic - [x] **Action 2.2.1.2**: Ensure backend handles all filter params - **Scope**: `veza-backend-api/internal/handlers/tracks.go` - Verify search, genre, format, sort handled - **Dependencies**: Action 2.2.1.1 complete (verification is independent, can be done safely) - **Risk**: LOW - **Validation**: ✅ Created BACKEND_FILTER_PARAMS_AUDIT.md. Verified backend `/tracks` endpoint handles: page, limit, user_id, genre, format, sort_by, sort_order. ⚠️ Missing: `search` parameter (frontend sends `search` but backend doesn't process it). Separate `/tracks/search` endpoint exists but uses `q` parameter. Recommendation: Add search support to ListTracks or use search endpoint. - **Rollback**: N/A (backend verification) - [x] **Action 2.2.1.3**: Handle LibraryPage.tsx vs LibraryPagePremium.tsx duplication - **Scope**: Audit both files - Determine which is active, consolidate or remove duplicate - **Dependencies**: None ✅ - **Risk**: MEDIUM (may break routing) - Verified safe: LibraryPagePremium.tsx not imported - **Validation**: ✅ Updated LIBRARY_PAGE_AUDIT.md. Verified LibraryPage.tsx is active (imported via LazyLibrary). LibraryPagePremium.tsx is duplicate/unused (not imported, older version without debounce). Removed LibraryPagePremium.tsx. Routing uses LibraryPage.tsx, no breakage. - **Rollback**: Restore LibraryPagePremium.tsx from git - [x] **Action 2.2.1.4**: Verify LibraryPage.tsx.old is not imported - **Scope**: Search codebase for imports of LibraryPage.tsx.old - Verify no references - **Dependencies**: Action 2.2.1.3 complete (verification is independent, can be done safely) - **Risk**: LOW 🔒 - **Validation**: ✅ Created LIBRARY_PAGE_AUDIT.md - Verified no imports of LibraryPage.tsx.old found. File is safe to delete. Also identified LibraryPagePremium.tsx as duplicate/unused. - **Rollback**: N/A (verification) - [x] **Action 2.2.1.5**: Remove LibraryPage.tsx.old if obsolete - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx.old` - Delete if not needed - **Dependencies**: Action 2.2.1.4 complete ✅ - **Risk**: LOW - **Validation**: ✅ File deleted - Verified no imports/references in Action 2.2.1.4, safe to remove - **Rollback**: Restore from git ### Sub-Epic 2.3: Unified Cache Invalidation 🟡 #### Task 2.3.1: Sync React Query Cache Across Tabs - [ ] **Action 2.3.1.1**: Create React Query sync utility - **Scope**: `apps/web/src/utils/reactQuerySync.ts` (create) - Use BroadcastChannel - **Dependencies**: None - **Risk**: MEDIUM - **Validation**: Cache updates sync across tabs - **Rollback**: Delete utility - [ ] **Action 2.3.1.2**: Integrate sync into query client setup - **Scope**: `apps/web/src/app/App.tsx` - Initialize sync on mount - **Dependencies**: Action 2.3.1.1 complete - **Risk**: LOW - **Validation**: Multi-tab updates work - **Rollback**: Remove initialization - [ ] **Action 2.3.1.3**: Handle broadcastSync message conflicts - **Scope**: `apps/web/src/utils/broadcastSync.ts:72-279` - Ensure React Query sync doesn't conflict with Zustand sync - **Dependencies**: Action 2.3.1.1 complete - **Risk**: MEDIUM - **Validation**: Both syncs work without conflicts - **Rollback**: Remove React Query sync - [ ] **Action 2.3.1.4**: Add message deduplication for React Query sync - **Scope**: `apps/web/src/utils/reactQuerySync.ts` - Use message IDs to prevent duplicate processing - **Dependencies**: Action 2.3.1.1 complete - **Risk**: LOW - **Validation**: No duplicate cache updates - **Rollback**: Remove deduplication ### Sub-Epic 2.5: Audit API Client Utilities 🟢 #### Task 2.5.1: Verify API Client Utilities - [x] **Action 2.5.1.1**: Audit requestDeduplication utility - **Scope**: `apps/web/src/services/requestDeduplication.ts` - Verify it works correctly, document usage - **Dependencies**: None ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Created REQUEST_DEDUPLICATION_AUDIT.md - Verified implementation, documented usage patterns, identified potential issues (FormData handling, cache size), noted utility may not be actively used - **Rollback**: N/A (audit) - [x] **Action 2.5.1.2**: Audit responseCache utility - **Scope**: `apps/web/src/services/responseCache.ts` - Verify it works correctly, document usage - **Dependencies**: None ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Created RESPONSE_CACHE_AUDIT.md - Verified implementation, documented cache configuration, validation logic, identified potential issues (limited usage, no automatic invalidation), noted cache only used via deduplicatedApiClient - **Rollback**: N/A (audit) - [x] **Action 2.5.1.3**: Audit offlineQueue utility - **Scope**: `apps/web/src/services/offlineQueue.ts` - Verify it works correctly, document usage - **Dependencies**: None ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Created OFFLINE_QUEUE_AUDIT.md - Verified implementation, documented queue management, priority system, persistence. Verified integration in error interceptor (line 999-1007) and UI (OfflineIndicator). Identified potential issues (serialization, token refresh). - **Rollback**: N/A (audit) - [ ] **Action 2.5.1.4**: Add UI for offline queue management - **Scope**: `apps/web/src/components/OfflineQueueManager.tsx` (create) - Show queued requests, allow retry/cancel - **Dependencies**: Action 2.5.1.3 complete - **Risk**: LOW 🔒 - **Validation**: UI shows queued requests - **Rollback**: Delete component - [ ] **Action 2.5.1.5**: Integrate offline queue UI with OfflineIndicator - **Scope**: `apps/web/src/components/OfflineIndicator.tsx` - Show queue status, link to manager - **Dependencies**: Action 2.5.1.4 complete - **Risk**: LOW 🔒 - **Validation**: Indicator shows queue status - **Rollback**: Remove queue status - [x] **Action 2.5.1.6**: Test request deduplication works correctly - **Scope**: `apps/web/src/services/requestDeduplication.ts` - Add tests or manual verification - **Dependencies**: Action 2.5.1.1 complete ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Created requestDeduplication.test.ts with comprehensive tests: promise sharing, different requests, query params, POST requests, cache cleanup, error handling, cache stats, cleanup - **Rollback**: N/A (testing) - [x] **Action 2.5.1.7**: Test response cache works correctly - **Scope**: `apps/web/src/services/responseCache.ts` - Add tests or manual verification - **Dependencies**: Action 2.5.1.2 complete ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Created responseCache.test.ts with 18 comprehensive tests - All passing. Tests cover: GET-only caching, cache expiration, Cache-Control directives, invalidation patterns, size limits, cleanup, stats - **Rollback**: N/A (testing) - [x] **Action 2.5.1.8**: Test offline queue works correctly - **Scope**: `apps/web/src/services/offlineQueue.ts` - Add tests or manual verification - **Dependencies**: Action 2.5.1.3 complete ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Created offlineQueue.test.ts with 24 comprehensive tests - All passing. Tests cover: request queuing (POST/PUT/DELETE/PATCH), priority system, queue processing, retry logic, persistence, localStorage integration, request filtering - **Rollback**: N/A (testing) - [x] **Action 2.5.1.9**: Add cache invalidation for response cache - **Scope**: `apps/web/src/services/responseCache.ts` - Ensure cache invalidated on mutations - **Dependencies**: Action 2.5.1.2 complete ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Already implemented - Verified `invalidateStateAfterMutation` called in response interceptor (line 493) for all mutations. Cache invalidated via `stateInvalidation.ts` with pattern-based and resource-specific invalidation. - **Rollback**: N/A (already implemented) - [x] **Action 2.5.1.10**: Add cache size limits - **Scope**: `apps/web/src/services/responseCache.ts` - Limit cache size, evict old entries - **Dependencies**: Action 2.5.1.2 complete ✅ - **Risk**: LOW 🔒 - **Validation**: ✅ Already implemented - Verified `maxSize = 100` with FIFO eviction (lines 240-246). Oldest entry removed when limit reached. Configurable via constructor. - **Rollback**: N/A (already implemented) ### Sub-Epic 2.4: Request Debouncing 🟢 #### Task 2.4.1: Add Debounce to Search Inputs - [x] **Action 2.4.1.1**: Install `use-debounce` or implement custom hook - **Scope**: `apps/web/package.json` - Add dependency (if using library) - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: ✅ Custom `useDebounce` hook already exists at `apps/web/src/hooks/useDebounce.ts` with tests. No external package needed. - **Rollback**: N/A (custom implementation) - [x] **Action 2.4.1.2**: Add debounce to LibraryPage search - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx:322-327` - Debounce `setSearchTerm` - **Dependencies**: Action 2.4.1.1 complete ✅ - **Risk**: LOW - **Validation**: ✅ Added `useDebounce` hook with 300ms delay. Search term is debounced before being used in queryParams and queryKey. Search fires 300ms after typing stops. - **Rollback**: Remove debounce hook usage - [x] **Action 2.4.1.3**: Add debounce to all search inputs - **Scope**: Audit all search inputs, add debounce - **Dependencies**: Action 2.4.1.2 complete ✅ - **Risk**: LOW - **Validation**: ✅ Created SEARCH_DEBOUNCE_AUDIT.md. Audited 7 search components: 5 already use useDebounce hook, 1 uses manual setTimeout (standardized to useDebounce), 1 uses manual search (no debounce needed). All automatic search inputs now use debouncing consistently. - **Rollback**: Remove debounce from each - [x] **Action 2.4.1.4**: Fix race condition in LibraryPage search/page reset - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx:116-120` - Use debounced search term for page reset - **Dependencies**: Action 2.4.1.2 complete ✅ - **Risk**: LOW - **Validation**: ✅ Updated useEffect to use `debouncedSearchTerm` instead of `searchTerm` for page reset. Page resets only after debounce completes, fixing race condition. - **Rollback**: Restore original useEffect --- ## EPIC 3: ERROR PROPAGATION 🟡 **Priority**: CORRECTNESS SECOND **Goal**: Standardize error display, improve recovery, reduce cognitive load ### Sub-Epic 3.1: Standard Error Component 🟡 #### Task 3.1.1: Create ErrorDisplay Component - [x] **Action 3.1.1.1**: Design ErrorDisplay component API - **Scope**: Document props: `error`, `onRetry`, `showDetails`, `context` - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: ✅ Created ERROR_DISPLAY_COMPONENT_API.md - Designed comprehensive API with props: error, onRetry, onDismiss, showDetails, context, variant, severity, size, actions. Defined error type normalization, default behavior, usage examples, integration points, accessibility requirements. - **Rollback**: N/A (documentation) - [x] **Action 3.1.1.2**: Implement ErrorDisplay component - **Scope**: `apps/web/src/components/ui/ErrorDisplay.tsx` (create) - **Dependencies**: Action 3.1.1.1 complete ✅ - **Risk**: LOW - **Validation**: ✅ Component implemented with all variants (inline, banner, modal, card), severities (error, warning, info), sizes (sm, md, lg), retry/dismiss actions, details toggle, error normalization, and accessibility features. Uses existing design system (Button, Card, Dialog, Alert patterns). TypeScript compilation passes. - **Rollback**: Delete component - [x] **Action 3.1.1.3**: Replace toast errors with ErrorDisplay in LibraryPage - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx:141-144` - Replace toast with ErrorDisplay - **Dependencies**: Action 3.1.1.2 complete ✅ - **Risk**: MEDIUM - **Validation**: ✅ Replaced query error display (lines 402-414) with ErrorDisplay card variant. Replaced 3 toast.error calls with ErrorDisplay banner variant for mutation errors. Added mutationError state to track and display mutation errors. Query errors show inline with retry button, mutation errors show as dismissible banner. - **Rollback**: Restore toast - [x] **Action 3.1.1.4**: Audit all error display patterns - **Scope**: Search codebase for `toast.error`, `toast.error()`, inline error divs - Document all patterns - **Dependencies**: Action 3.1.1.3 complete ✅ - **Risk**: LOW - **Validation**: ✅ Created ERROR_DISPLAY_PATTERNS_AUDIT.md documenting 35+ toast.error() calls across 20+ files, 10+ inline error divs, 3 dedicated error components, 2 error boundaries, and 15+ form validation errors. Categorized by error type (query, mutation, validation, network, runtime, player) and prioritized (HIGH/MEDIUM/LOW). Provided recommendations for each pattern. - **Rollback**: N/A (audit) - [x] **Action 3.1.1.5**: Create error display strategy document - **Scope**: `apps/web/src/components/ui/ERROR_DISPLAY_STRATEGY.md` (create) - Document when to use toast vs ErrorDisplay - **Dependencies**: Action 3.1.1.4 complete ✅ - **Risk**: LOW - **Validation**: ✅ Created comprehensive strategy document with decision tree, variant/severity selection guides, migration strategy (5 phases), code examples, accessibility considerations, testing strategy, and rollback plan. Defines when to use ErrorDisplay (query errors, mutation errors, network errors, runtime errors) vs toast (transient actions, quick feedback) vs inline validation (form errors). - **Rollback**: Delete file - [x] **Action 3.1.1.6**: Replace all toast.error() calls with ErrorDisplay (where appropriate) - **Scope**: All locations from Action 3.1.1.4 - Replace based on strategy - **Dependencies**: Action 3.1.1.5 complete ✅ - **Risk**: MEDIUM - **Validation**: ✅ Replaced toast.error() calls in 13 files: TrackDetailPage, MarketplaceHome, RolesPage, SettingsPage (query errors), ShareDialog, CommentSection, SharePlaylistModal, AddCollaboratorModal, ChatSidebar, CreateRoomDialog, ProfileForm, AccountSettings, Cart (mutation errors). Kept toast for copy link errors (transient actions) and form validation errors (per strategy). API client (api/client.ts) toast.error calls remain as they require global error state/store architecture - documented as separate consideration. - **Rollback**: Restore toast calls - [x] **Action 3.1.1.7**: Remove duplicate error displays - **Scope**: Components showing both toast and inline error - Remove duplicates - **Dependencies**: Action 3.1.1.6 complete ✅ - **Risk**: LOW - **Validation**: ✅ Verified no duplicate error displays in modified files. All toast.error() calls have been replaced with ErrorDisplay, and remaining toast.error() calls are appropriate (copy link = transient, validation = form validation). No components show both toast and ErrorDisplay for the same error. - **Rollback**: Restore duplicates - [x] **Action 3.1.1.8**: Update error display in AuthErrorMessage component - **Scope**: `apps/web/src/features/auth/components/AuthErrorMessage.tsx` - Use ErrorDisplay if appropriate - **Dependencies**: Action 3.1.1.2 complete ✅ - **Risk**: LOW - **Validation**: ✅ Updated AuthErrorMessage to use ErrorDisplay inline variant internally while maintaining backward compatibility with existing API (message, className, id props). Component now wraps ErrorDisplay with auth context. Added deprecation notice for future migration to direct ErrorDisplay usage. - **Rollback**: Restore original component - [x] **Action 3.1.1.9**: Update error display in PlayerError component - **Scope**: `apps/web/src/features/player/components/PlayerError.tsx` - Use ErrorDisplay if appropriate - **Dependencies**: Action 3.1.1.2 complete ✅ - **Risk**: LOW - **Validation**: ✅ Updated PlayerError to use ErrorDisplay card variant internally while maintaining backward compatibility. Preserved error type detection logic (network, decode, source, abort, unknown) and custom error messages. ErrorDisplay now handles retry functionality and dev details. Component maintains same API (error, errorType, onRetry, className, showRetry, retryLabel) for backward compatibility. - **Rollback**: Restore original component - [x] **Action 3.1.1.10**: Update error display in PlaylistErrorBoundary - **Scope**: `apps/web/src/features/playlists/components/PlaylistErrorBoundary.tsx` - Use ErrorDisplay - **Dependencies**: Action 3.1.1.2 complete ✅ - **Risk**: LOW - **Validation**: ✅ Updated ErrorFallback component in PlaylistErrorBoundary to use ErrorDisplay card variant. Preserved retry functionality (onReset) and "Retour aux playlists" navigation action. ErrorDisplay now handles error presentation, retry button, and dev details. Removed unused imports (Button, Home, Card components). - **Rollback**: Restore original component ### Sub-Epic 3.2: Error Categories 🟡 #### Task 3.2.1: Implement Error Categorization - [x] **Action 3.2.1.1**: Add error category detection - **Scope**: `apps/web/src/utils/apiErrorHandler.ts` - Add `getErrorCategory()` function - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: ✅ Added `getErrorCategory()` function that categorizes errors into: network, validation, authentication, authorization, not_found, rate_limit, server_error, timeout, unknown. Function handles ApiError, Error objects, and AxiosError patterns. Categorization based on HTTP status codes, error codes, and error message/name patterns. TypeScript compilation passes. - **Rollback**: Remove function - [x] **Action 3.2.1.2**: Network errors show offline indicator - **Scope**: `apps/web/src/components/OfflineIndicator.tsx` - Show when network error - **Dependencies**: Action 3.2.1.1 complete ✅ - **Risk**: LOW - **Validation**: Indicator appears on network errors ✅ - **Rollback**: Remove indicator logic - [x] **Action 3.2.1.3**: Validation errors highlight fields - **Scope**: Form components - Highlight invalid fields from error.details - **Dependencies**: Action 3.2.1.1 complete ✅ - **Risk**: MEDIUM - **Validation**: Invalid fields highlighted ✅ - **Rollback**: Remove highlighting - [x] **Action 3.2.1.4**: Auth errors redirect to login - **Scope**: `apps/web/src/services/api/client.ts` - Intercept 401, redirect - **Dependencies**: Action 3.2.1.1 complete ✅ - **Risk**: LOW - **Validation**: 401 errors redirect ✅ - **Rollback**: Remove redirect logic - [x] **Action 3.2.1.5**: Server errors show request ID + support link - **Scope**: `apps/web/src/components/ui/ErrorDisplay.tsx` - Show request ID, add "Report Issue" button - **Dependencies**: Action 3.1.1.2 complete ✅ - **Risk**: LOW - **Validation**: Request ID visible, support link works ✅ - **Rollback**: Remove request ID display - [x] **Action 3.2.1.6**: Create support issue reporting utility - **Scope**: `apps/web/src/utils/reportIssue.ts` (create) - Format issue with request ID, error details - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: Utility formats issue report correctly ✅ - **Rollback**: Delete utility - [x] **Action 3.2.1.7**: Integrate issue reporting with ErrorDisplay - **Scope**: `apps/web/src/components/ui/ErrorDisplay.tsx` - Use reportIssue utility - **Dependencies**: Action 3.2.1.6 complete ✅ - **Risk**: LOW - **Validation**: "Report Issue" button uses utility ✅ - **Rollback**: Remove integration ### Sub-Epic 3.3: Error Boundaries 🟢 #### Task 3.3.1: Wrap Routes in Error Boundaries - [x] **Action 3.3.1.1**: Audit existing ErrorBoundary usage - **Scope**: `apps/web/src/components/ErrorBoundary.tsx` - Check where used - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: List of routes without boundaries ✅ - **Rollback**: N/A (read-only) - [x] **Action 3.3.1.2**: Wrap all routes in ErrorBoundary - **Scope**: `apps/web/src/app/App.tsx` or router config - Add ErrorBoundary to each route - **Dependencies**: Action 3.3.1.1 complete ✅ - **Risk**: LOW - **Validation**: All routes have error boundaries ✅ (verified in audit) - **Rollback**: Remove boundaries - [x] **Action 3.3.1.3**: Update ErrorBoundary to use ErrorDisplay - **Scope**: `apps/web/src/components/ErrorBoundary.tsx` - Use ErrorDisplay component for rendering - **Dependencies**: Action 3.1.1.2 complete ✅ - **Risk**: LOW - **Validation**: ErrorBoundary uses ErrorDisplay ✅ - **Rollback**: Restore original ErrorBoundary rendering - [x] **Action 3.3.1.4**: Add error boundary logging - **Scope**: `apps/web/src/components/ErrorBoundary.tsx` - Log errors to monitoring service - **Dependencies**: Action 3.3.1.2 complete ✅ - **Risk**: LOW - **Validation**: Errors logged to monitoring ✅ - **Rollback**: Remove logging ### Sub-Epic 3.4: Retry Logic 🟢 #### Task 3.4.1: Add Retry UI to Error States - [x] **Action 3.4.1.1**: Add retry button to ErrorDisplay - **Scope**: `apps/web/src/components/ui/ErrorDisplay.tsx` - Add retry button, call `onRetry` - **Dependencies**: Action 3.1.1.2 complete ✅ - **Risk**: LOW - **Validation**: Retry button works ✅ (already implemented) - **Rollback**: Remove button - [x] **Action 3.4.1.2**: Audit all mutation error handlers - **Scope**: Search for mutation error handlers - List all mutation error handlers - **Dependencies**: Action 3.4.1.1 complete ✅ - **Risk**: LOW 🔒 - **Validation**: Complete list of mutation error handlers ✅ - **Rollback**: N/A (audit) - [x] **Action 3.4.1.3**: Implement retry for failed mutations - **Scope**: All handlers from Action 3.4.1.2 - Add retry logic - **Dependencies**: Action 3.4.1.2 complete ✅ - **Risk**: MEDIUM - **Validation**: Failed mutations can be retried ✅ - **Rollback**: Remove retry logic - **Status**: ✅ Complete - Retry logic implemented for all 13 mutation handlers with max 3 retries enforced - [x] **Action 3.4.1.4**: Add retry count limit - **Scope**: Retry logic - Limit retry attempts (max 3) - **Dependencies**: Action 3.4.1.3 complete ✅ - **Risk**: LOW 🔒 - **Validation**: Retries limited to 3 attempts ✅ - **Rollback**: Remove limit - **Status**: ✅ Complete - Max 3 retries enforced in all handlers (implemented as part of 3.4.1.3) ### Sub-Epic 3.5: Improve Network Error Messages 🟢 #### Task 3.5.1: Distinguish Network Error Types - [x] **Action 3.5.1.1**: Enhance network error detection - **Scope**: `apps/web/src/utils/apiErrorHandler.ts:108-122` - Distinguish timeout vs connection refused vs offline - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: Different messages for different error types ✅ - **Rollback**: Restore generic message - **Status**: ✅ Complete - Enhanced to detect ECONNREFUSED, ENETUNREACH, ERR_NETWORK, and navigator.onLine for offline detection - [x] **Action 3.5.1.2**: Add offline detection utility - **Scope**: `apps/web/src/utils/offlineDetection.ts` (create) - Use navigator.onLine API - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: Utility detects online/offline state ✅ - **Rollback**: Delete utility - **Status**: ✅ Complete - Created utility with isOnline(), isOffline(), subscribeToOnlineStatus(), and network info helpers - [x] **Action 3.5.1.3**: Integrate offline detection with error handler - **Scope**: `apps/web/src/utils/apiErrorHandler.ts` - Use offline detection for network errors - **Dependencies**: Action 3.5.1.2 complete ✅ - **Risk**: LOW - **Validation**: Errors distinguish offline vs server down ✅ - **Rollback**: Remove offline detection integration - **Status**: ✅ Complete - Integrated isOffline() utility in network error detection --- ## EPIC 4: STATE OWNERSHIP 🟡 **Priority**: CORRECTNESS SECOND **Goal**: Single source of truth, eliminate duplication, prevent desync ### Sub-Epic 4.1: Migrate Domain Data to React Query 🟡 #### Task 4.1.1: Remove User from Zustand Auth Store - [x] **Action 4.1.1.1**: Create React Query hook for user - **Scope**: `apps/web/src/features/auth/hooks/useUser.ts` (create) - `useQuery(['user', 'me'], getMe)` - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: Hook returns user data ✅ - **Rollback**: Delete hook - **Status**: ✅ Complete - Created useUser hook with React Query, enabled only when authenticated, with proper caching - [ ] **Action 4.1.1.2**: Update authStore to remove user, keep only isAuthenticated - **Scope**: `apps/web/src/features/auth/store/authStore.ts` - Remove `user` field, keep boolean - **Dependencies**: Action 4.1.1.1 complete - **Risk**: HIGH (breaking change) - **Validation**: Store only has `isAuthenticated`, no `user` - **Rollback**: Restore `user` field - [ ] **Action 4.1.1.3**: Audit all files using `useAuthStore().user` - **Scope**: Search codebase for `useAuthStore().user`, `useAuthStore.getState().user`, document all locations - **Dependencies**: Action 4.1.1.2 complete - **Risk**: LOW - **Validation**: Complete list of files using user from store - **Rollback**: N/A (audit) - [ ] **Action 4.1.1.4**: Replace all `useAuthStore().user` with `useUser()` - **Scope**: All files from Action 4.1.1.3 - Replace with `useUser()` hook - **Dependencies**: Action 4.1.1.3 complete - **Risk**: HIGH - **Validation**: No references to `useAuthStore().user`, all use `useUser()` - **Rollback**: Restore references - [ ] **Action 4.1.1.5**: Update components that destructure user from store - **Scope**: Components using `const { user } = useAuthStore()` - Update to use hook - **Dependencies**: Action 4.1.1.4 complete - **Risk**: MEDIUM - **Validation**: All destructuring updated - **Rollback**: Restore destructuring #### Task 4.1.2: Remove Tracks from Zustand Library Store - [ ] **Action 4.1.2.1**: Verify React Query handles all track queries - **Scope**: Audit `apps/web/src/features/tracks/` - Ensure all queries use React Query - **Dependencies**: None - **Risk**: LOW - **Validation**: All track data from React Query - **Rollback**: N/A (verification) - [ ] **Action 4.1.2.2**: Audit library store for all domain data - **Scope**: `apps/web/src/stores/library.ts` - List all fields: `items`, `favorites`, `pagination`, `filters` - **Dependencies**: Action 4.1.2.1 complete - **Risk**: LOW - **Validation**: Complete list of domain data fields - **Rollback**: N/A (audit) - [ ] **Action 4.1.2.3**: Determine which fields are UI state vs domain data - **Scope**: `apps/web/src/stores/library.ts` - Categorize: UI state (filters, view preferences) vs domain (items, favorites) - **Dependencies**: Action 4.1.2.2 complete - **Risk**: LOW - **Validation**: Fields categorized - **Rollback**: N/A (categorization) - [ ] **Action 4.1.2.4**: Remove domain data from library store - **Scope**: `apps/web/src/stores/library.ts` - Remove `items`, `favorites`, `pagination`, keep only `filters` (UI state) - **Dependencies**: Action 4.1.2.3 complete, React Query handles all domain data - **Risk**: HIGH - **Validation**: Store has no domain data, only UI state - **Rollback**: Restore domain data fields - [ ] **Action 4.1.2.5**: Remove undoRedo middleware from library store - **Scope**: `apps/web/src/stores/library.ts:64` - Remove `undoRedo()` wrapper - **Dependencies**: Action 4.1.2.4 complete (no domain data to undo) - **Risk**: MEDIUM - **Validation**: No undoRedo middleware - **Rollback**: Restore undoRedo wrapper - [ ] **Action 4.1.2.6**: Remove stateNormalization from library store - **Scope**: `apps/web/src/stores/library.ts` - Remove normalized state, use React Query normalization - **Dependencies**: Action 4.1.2.4 complete - **Risk**: MEDIUM - **Validation**: No normalization utilities used - **Rollback**: Restore normalization - [ ] **Action 4.1.2.7**: Remove stateMiddleware from library store - **Scope**: `apps/web/src/stores/library.ts:65` - Remove `stateMiddleware()` wrapper - **Dependencies**: Action 4.1.2.4 complete - **Risk**: MEDIUM - **Validation**: No stateMiddleware wrapper - **Rollback**: Restore stateMiddleware - [ ] **Action 4.1.2.8**: Update all components using library store domain data - **Scope**: All components using `useLibraryStore().items`, `.favorites`, `.pagination` - Replace with React Query - **Dependencies**: Action 4.1.2.4 complete - **Risk**: HIGH - **Validation**: No components access domain data from store - **Rollback**: Restore store access ### Sub-Epic 4.2: Unified Cache Invalidation 🟢 #### Task 4.2.1: Sync React Query Cache with BroadcastChannel - [ ] **Action 4.2.1.1**: Extend broadcastSync to invalidate React Query - **Scope**: `apps/web/src/utils/broadcastSync.ts` - Add React Query invalidation on state update - **Dependencies**: Action 2.3.1.1 complete - **Risk**: MEDIUM - **Validation**: Zustand updates trigger React Query invalidation - **Rollback**: Remove invalidation logic ### Sub-Epic 4.3: Simplify Auth State 🟢 #### Task 4.3.1: Remove Complex Deduplication Logic - [x] **Action 4.3.1.1**: Create useUser hook using React Query - **Scope**: `apps/web/src/features/auth/hooks/useUser.ts` - Use `useQuery(['user', 'me'], getMe)` - **Dependencies**: Action 4.1.1.1 complete ✅ - **Risk**: LOW - **Validation**: Hook uses React Query, has built-in deduplication ✅ - **Rollback**: Delete hook - **Status**: ✅ Complete - Same as Action 4.1.1.1, hook already created - [ ] **Action 4.3.1.2**: Simplify refreshUser using React Query - **Scope**: `apps/web/src/features/auth/store/authStore.ts:142-218` - Remove manual deduplication, use React Query hook - **Dependencies**: Action 4.3.1.1 complete - **Risk**: MEDIUM - **Validation**: No manual promise deduplication needed - **Rollback**: Restore deduplication logic - [ ] **Action 4.3.1.3**: Remove _refreshUserPromise field - **Scope**: `apps/web/src/features/auth/store/authStore.ts:20` - Remove field, no longer needed - **Dependencies**: Action 4.3.1.2 complete - **Risk**: LOW 🔒 - **Validation**: Field removed, no references - **Rollback**: Restore field ### Sub-Epic 4.4: Standardize Optimistic Updates 🟢 #### Task 4.4.1: Use React Query's onMutate - [x] **Action 4.4.1.1**: Audit custom optimistic updates - **Scope**: `apps/web/src/utils/optimisticStoreUpdates.ts` - List all optimistic logic - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: List of optimistic updates ✅ - **Rollback**: N/A (audit) - **Status**: ✅ Complete - Documented 3 utilities (createOptimisticStoreUpdate, withOptimisticUpdate, withOptimisticArrayUpdate). File is **unused** (no imports found). See `apps/web/docs/OPTIMISTIC_STORE_UPDATES_AUDIT.md` - [ ] **Action 4.4.1.2**: Migrate to React Query's onMutate - **Scope**: All mutations - Use `onMutate` instead of custom logic - **Dependencies**: Action 4.4.1.1 complete - **Risk**: MEDIUM - **Validation**: All optimistic updates use React Query - **Rollback**: Restore custom logic - [ ] **Action 4.4.1.3**: Delete optimisticStoreUpdates.ts - **Scope**: Delete `apps/web/src/utils/optimisticStoreUpdates.ts` - **Dependencies**: Action 4.4.1.2 complete - **Risk**: LOW - **Validation**: File deleted, no imports - **Rollback**: Restore from git - [x] **Action 4.4.1.4**: Audit all mutations for optimistic updates - **Scope**: Search codebase for mutations - List all mutations, check which have optimistic updates - **Dependencies**: None ✅ - **Risk**: LOW - **Validation**: Complete list of mutations with optimistic status ✅ - **Rollback**: N/A (audit) - **Status**: ✅ Complete - Audited 20+ mutations. Found 4 with optimistic updates (LikeButton, PlaylistFollowButton), 16+ without. See `apps/web/docs/MUTATIONS_OPTIMISTIC_UPDATES_AUDIT.md` - [ ] **Action 4.4.1.5**: Add optimistic updates to mutations missing them - **Scope**: Mutations without optimistic updates - Add onMutate logic - **Dependencies**: Action 4.4.1.4 complete - **Risk**: MEDIUM - **Validation**: All mutations have optimistic updates - **Rollback**: Remove optimistic updates ### Sub-Epic 4.5: Audit All Zustand Stores 🟡 #### Task 4.5.1: Audit stores for domain data - [ ] **Action 4.5.1.1**: List all Zustand stores - **Scope**: Search for `create<` in `apps/web/src/stores/` and `apps/web/src/features/*/store/` - List all stores - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Complete list of all stores - **Rollback**: N/A (audit) - [ ] **Action 4.5.1.2**: Categorize stores: UI state vs domain data - **Scope**: All stores from Action 4.5.1.1 - Categorize each store - **Dependencies**: Action 4.5.1.1 complete - **Risk**: LOW 🔒 - **Validation**: All stores categorized - **Rollback**: N/A (categorization) - [ ] **Action 4.5.1.3**: Check for stores/auth.ts (should be removed) - **Scope**: `apps/web/src/stores/auth.ts` (if exists) - Verify it's obsolete - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: File doesn't exist or is unused - **Rollback**: N/A (verification) - [ ] **Action 4.5.1.4**: Remove stores/auth.ts if obsolete - **Scope**: `apps/web/src/stores/auth.ts` - Delete if unused - **Dependencies**: Action 4.5.1.3 complete, verify no imports - **Risk**: MEDIUM - **Validation**: File deleted, no imports remain - **Rollback**: Restore from git - [ ] **Action 4.5.1.5**: Handle stores/chat.ts vs features/chat/store/chatStore.ts duplication - **Scope**: Both files - Determine which is active, consolidate or remove duplicate - **Dependencies**: Action 4.5.1.1 complete - **Risk**: MEDIUM (may break chat functionality) - **Validation**: Single chat store, chat works - **Rollback**: Restore both files - [ ] **Action 4.5.1.6**: Audit cartStore for domain data - **Scope**: `apps/web/src/stores/cartStore.ts` - Check if stores domain data (cart items) - **Dependencies**: Action 4.5.1.2 complete - **Risk**: LOW 🔒 - **Validation**: Cart store categorized - **Rollback**: N/A (audit) - [ ] **Action 4.5.1.7**: Migrate cartStore domain data to React Query (if needed) - **Scope**: `apps/web/src/stores/cartStore.ts` - Move cart items to React Query if domain data - **Dependencies**: Action 4.5.1.6 complete - **Risk**: MEDIUM - **Validation**: Cart items in React Query, store only UI state - **Rollback**: Restore cart items to store - [ ] **Action 4.5.1.8**: Audit playerStore for domain data - **Scope**: `apps/web/src/features/player/store/playerStore.ts` - Check if stores domain data - **Dependencies**: Action 4.5.1.2 complete - **Risk**: LOW 🔒 - **Validation**: Player store categorized - **Rollback**: N/A (audit) - [ ] **Action 4.5.1.9**: Migrate playerStore domain data to React Query (if needed) - **Scope**: `apps/web/src/features/player/store/playerStore.ts` - Move domain data to React Query - **Dependencies**: Action 4.5.1.8 complete - **Risk**: MEDIUM - **Validation**: Domain data in React Query, store only player UI state - **Rollback**: Restore domain data to store - [ ] **Action 4.5.1.10**: Verify uiStore only has UI state - **Scope**: `apps/web/src/stores/ui.ts` - Verify only UI preferences (theme, language, sidebar) - **Dependencies**: Action 4.5.1.2 complete - **Risk**: LOW 🔒 - **Validation**: UI store has no domain data - **Rollback**: N/A (verification) ### Sub-Epic 4.6: Clean Up State Utilities 🟢 #### Task 4.6.1: Audit state utility files - [ ] **Action 4.6.1.1**: List all state utility files - **Scope**: `apps/web/src/utils/state*.ts`, `apps/web/src/utils/*state*.ts` - List all utilities - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Complete list of state utilities - **Rollback**: N/A (audit) - [ ] **Action 4.6.1.2**: Determine which utilities are still needed - **Scope**: All utilities from Action 4.6.1.1 - Categorize: needed, redundant, obsolete - **Dependencies**: Action 4.6.1.1 complete, Epic 4 complete - **Risk**: LOW 🔒 - **Validation**: Utilities categorized - **Rollback**: N/A (categorization) - [ ] **Action 4.6.1.3**: Remove obsolete state utilities - **Scope**: Obsolete utilities from Action 4.6.1.2 - Delete unused files - **Dependencies**: Action 4.6.1.2 complete, verify no imports - **Risk**: MEDIUM - **Validation**: Obsolete utilities deleted, no imports - **Rollback**: Restore from git - [ ] **Action 4.6.1.4**: Simplify stateMiddleware if still needed - **Scope**: `apps/web/src/utils/stateMiddleware.ts` - Simplify or remove if redundant - **Dependencies**: Action 4.6.1.2 complete - **Risk**: MEDIUM - **Validation**: Middleware simplified or removed - **Rollback**: Restore original middleware - [ ] **Action 4.6.1.5**: Update stateInvalidation to work with React Query - **Scope**: `apps/web/src/utils/stateInvalidation.ts` - Update to invalidate React Query cache - **Dependencies**: Epic 4 complete - **Risk**: MEDIUM - **Validation**: Invalidation works with React Query - **Rollback**: Restore original invalidation - [ ] **Action 4.6.1.6**: Update statePersistence if still needed - **Scope**: `apps/web/src/utils/statePersistence.ts` - Ensure it doesn't conflict with React Query persistence - **Dependencies**: Epic 4 complete - **Risk**: MEDIUM - **Validation**: No conflicts with React Query - **Rollback**: Restore original persistence - [ ] **Action 4.6.1.7**: Update STATE_DEBUGGING.md documentation - **Scope**: `apps/web/src/docs/STATE_DEBUGGING.md` - Remove references to stores/auth.ts, update store list - **Dependencies**: Action 4.5.1.4 complete - **Risk**: LOW 🔒 - **Validation**: Documentation accurate - **Rollback**: Restore original documentation - [ ] **Action 4.6.1.8**: Update STATE_SELECTORS.md documentation - **Scope**: `apps/web/src/docs/STATE_SELECTORS.md` - Update selectors for new store structure - **Dependencies**: Epic 4 complete - **Risk**: LOW 🔒 - **Validation**: Documentation accurate - **Rollback**: Restore original documentation - [ ] **Action 4.6.1.9**: Audit undoRedo utility usage - **Scope**: `apps/web/src/utils/undoRedo.ts` - Check where used, determine if still needed - **Dependencies**: Action 4.1.2.5 complete - **Risk**: LOW 🔒 - **Validation**: Usage documented - **Rollback**: N/A (audit) - [ ] **Action 4.6.1.10**: Remove undoRedo if unused - **Scope**: `apps/web/src/utils/undoRedo.ts` - Delete if no longer used - **Dependencies**: Action 4.6.1.9 complete, verify no imports - **Risk**: MEDIUM - **Validation**: File deleted, no imports - **Rollback**: Restore from git - [ ] **Action 4.6.1.11**: Audit stateNormalization utility - **Scope**: `apps/web/src/utils/stateNormalization.ts` - Check if still needed with React Query - **Dependencies**: Action 4.1.2.6 complete - **Risk**: LOW 🔒 - **Validation**: Usage documented - **Rollback**: N/A (audit) - [ ] **Action 4.6.1.12**: Remove stateNormalization if unused - **Scope**: `apps/web/src/utils/stateNormalization.ts` - Delete if no longer used - **Dependencies**: Action 4.6.1.11 complete, verify no imports - **Risk**: MEDIUM - **Validation**: File deleted, no imports - **Rollback**: Restore from git - [ ] **Action 4.6.1.13**: Audit stateCleanup utility - **Scope**: `apps/web/src/utils/stateCleanup.ts` - Check if still needed - **Dependencies**: Epic 4 complete - **Risk**: LOW 🔒 - **Validation**: Usage documented - **Rollback**: N/A (audit) - [ ] **Action 4.6.1.14**: Update or remove stateCleanup utility - **Scope**: `apps/web/src/utils/stateCleanup.ts` - Update for React Query or remove if unused - **Dependencies**: Action 4.6.1.13 complete - **Risk**: MEDIUM - **Validation**: Utility updated or removed - **Rollback**: Restore original utility --- ## EPIC 5: SECURITY & ROBUSTNESS 🔴 **Priority**: STABILITY FIRST **Goal**: Fix XSS vulnerabilities, improve error correlation, add rate limit UI ### Sub-Epic 5.1: Secure Token Storage 🔴 #### Task 5.1.1: Move Access Token to httpOnly Cookie - [ ] **Action 5.1.1.1**: Backend: Set access token in httpOnly cookie - **Scope**: `veza-backend-api/internal/handlers/auth.go` - Set cookie on login/refresh - **Dependencies**: None - **Risk**: HIGH (breaking change) - **Validation**: Cookie set, accessible in browser - **Rollback**: Remove cookie, restore token in response body - [ ] **Action 5.1.1.2**: Frontend: Remove localStorage token storage - **Scope**: `apps/web/src/services/tokenStorage.ts` - Remove localStorage, read from cookie - **Dependencies**: Action 5.1.1.1 complete, backend deployed - **Risk**: HIGH - **Validation**: No tokens in localStorage, all from cookie - **Rollback**: Restore localStorage - [ ] **Action 5.1.1.3**: Update API client to read token from cookie - **Scope**: `apps/web/src/services/api/client.ts` - Remove Authorization header, rely on cookie - **Dependencies**: Action 5.1.1.2 complete - **Risk**: MEDIUM - **Validation**: Requests work without Authorization header - **Rollback**: Restore header logic **Alternative**: Short-lived tokens (5 min) + frequent refresh - [ ] **Action 5.1.1.4**: Reduce access token expiry to 5 minutes - **Scope**: `veza-backend-api/internal/services/jwt.go` - Change expiry - **Dependencies**: None - **Risk**: MEDIUM - **Validation**: Tokens expire in 5 min - **Rollback**: Restore original expiry - [ ] **Action 5.1.1.5**: Implement proactive refresh every 4 minutes - **Scope**: `apps/web/src/services/tokenRefresh.ts` - Refresh before expiry - **Dependencies**: Action 5.1.1.4 complete - **Risk**: LOW - **Validation**: Tokens refresh automatically - **Rollback**: Remove proactive refresh - [ ] **Action 5.1.1.6**: Clean up localStorage token references - **Scope**: Search codebase for `localStorage.getItem('access_token')`, `localStorage.setItem('access_token')` - Remove all - **Dependencies**: Action 5.1.1.2 complete - **Risk**: LOW - **Validation**: No localStorage token access remains - **Rollback**: Restore localStorage access - [ ] **Action 5.1.1.7**: Update TokenStorage to read from cookie only - **Scope**: `apps/web/src/services/tokenStorage.ts` - Remove localStorage, add cookie reading - **Dependencies**: Action 5.1.1.2 complete - **Risk**: MEDIUM - **Validation**: TokenStorage reads from cookie, no localStorage - **Rollback**: Restore localStorage logic - [ ] **Action 5.1.1.8**: Update tokenRefresh to work with cookies - **Scope**: `apps/web/src/services/tokenRefresh.ts` - Update to read/write cookies instead of localStorage - **Dependencies**: Action 5.1.1.7 complete - **Risk**: MEDIUM - **Validation**: Token refresh works with cookies - **Rollback**: Restore localStorage logic - [ ] **Action 5.1.1.9**: Update all token access to use TokenStorage - **Scope**: Search for direct localStorage token access - Replace with TokenStorage methods - **Dependencies**: Action 5.1.1.7 complete - **Risk**: MEDIUM - **Validation**: No direct localStorage token access - **Rollback**: Restore direct access ### Sub-Epic 5.2: Pre-Validation 🟢 #### Task 5.2.1: Add Backend Validation Endpoint - [ ] **Action 5.2.1.1**: Create `/api/v1/validate` endpoint - **Scope**: `veza-backend-api/internal/handlers/validate.go` (create) - **Dependencies**: None - **Risk**: LOW - **Validation**: Endpoint validates request bodies - **Rollback**: Delete endpoint - [ ] **Action 5.2.1.2**: Frontend: Call validate before submit - **Scope**: Form components - Call validate endpoint on blur/change - **Dependencies**: Action 5.2.1.1 complete - **Risk**: MEDIUM - **Validation**: Backend errors shown before submit - **Rollback**: Remove validation calls - [ ] **Action 5.2.1.3**: Create useFormValidation hook - **Scope**: `apps/web/src/hooks/useFormValidation.ts` (create) - Hook for pre-validation - **Dependencies**: Action 5.2.1.1 complete - **Risk**: LOW - **Validation**: Hook works, integrates with forms - **Rollback**: Delete hook - [ ] **Action 5.2.1.4**: Integrate useFormValidation into all forms - **Scope**: All form components - Use hook for pre-validation - **Dependencies**: Action 5.2.1.3 complete - **Risk**: MEDIUM - **Validation**: All forms use pre-validation - **Rollback**: Remove hook usage - [ ] **Action 5.2.1.5**: Debounce validation calls - **Scope**: `apps/web/src/hooks/useFormValidation.ts` - Debounce validation requests - **Dependencies**: Action 5.2.1.3 complete - **Risk**: LOW - **Validation**: Validation debounced (300ms) - **Rollback**: Remove debounce ### Sub-Epic 5.3: Error Correlation 🟢 #### Task 5.3.1: Always Show Request ID - [ ] **Action 5.3.1.1**: Remove dev-only check for request ID - **Scope**: `apps/web/src/utils/apiErrorHandler.ts:315-320` - Always include request ID - **Dependencies**: None - **Risk**: LOW - **Validation**: Request ID always shown - **Rollback**: Restore dev check - [ ] **Action 5.3.1.2**: Add "Report Issue" button with request ID - **Scope**: `apps/web/src/components/ui/ErrorDisplay.tsx` - Add button, copy request ID - **Dependencies**: Action 3.1.1.2 complete - **Risk**: LOW - **Validation**: Button copies request ID to clipboard - **Rollback**: Remove button ### Sub-Epic 5.4: Rate Limit UI 🟢 #### Task 5.4.1: Display Rate Limit Status - [ ] **Action 5.4.1.1**: Parse rate limit headers - **Scope**: `apps/web/src/services/api/client.ts` - Parse `X-RateLimit-*` headers - **Dependencies**: None - **Risk**: LOW - **Validation**: Headers parsed, stored in state - **Rollback**: Remove parsing - [ ] **Action 5.4.1.2**: Create rate limit indicator component - **Scope**: `apps/web/src/components/RateLimitIndicator.tsx` (create) - **Dependencies**: Action 5.4.1.1 complete - **Risk**: LOW - **Validation**: Component shows rate limit status - **Rollback**: Delete component - [ ] **Action 5.4.1.3**: Show indicator in header - **Scope**: `apps/web/src/components/Header.tsx` or layout - Add RateLimitIndicator - **Dependencies**: Action 5.4.1.2 complete - **Risk**: LOW - **Validation**: Indicator visible in header - **Rollback**: Remove from header - [ ] **Action 5.4.1.4**: Disable buttons when rate limited - **Scope**: All mutation buttons - Check rate limit, disable if limited - **Dependencies**: Action 5.4.1.1 complete - **Risk**: MEDIUM - **Validation**: Buttons disabled when rate limited - **Rollback**: Remove disable logic - [ ] **Action 5.4.1.5**: Show countdown timer - **Scope**: `apps/web/src/components/RateLimitIndicator.tsx` - Calculate reset time, show timer - **Dependencies**: Action 5.4.1.2 complete - **Risk**: LOW - **Validation**: Timer counts down to reset - **Rollback**: Remove timer - [ ] **Action 5.4.1.6**: Add rate limit state management - **Scope**: `apps/web/src/stores/rateLimit.ts` (create) - Store rate limit state from headers - **Dependencies**: Action 5.4.1.1 complete - **Risk**: LOW - **Validation**: Rate limit state stored and accessible - **Rollback**: Delete store - [ ] **Action 5.4.1.7**: Integrate rate limit store with indicator - **Scope**: `apps/web/src/components/RateLimitIndicator.tsx` - Use rate limit store - **Dependencies**: Action 5.4.1.6 complete - **Risk**: LOW - **Validation**: Indicator reads from store - **Rollback**: Remove store integration --- ## EPIC 6: SCALABILITY & EVOLUTION 🟢 **Priority**: UX/UI THIRD **Goal**: Reduce coupling, improve performance, enable evolution ### Sub-Epic 6.1: API Abstraction Layer 🟢 #### Task 6.1.1: Create Service Layer - [ ] **Action 6.1.1.1**: Create tracks API service - **Scope**: `apps/web/src/services/api/tracks.ts` (create) - Export `tracksApi` object - **Dependencies**: None - **Risk**: LOW - **Validation**: Service exports list, get, create, update, delete methods - **Rollback**: Delete file - [ ] **Action 6.1.1.2**: Replace direct API calls with service - **Scope**: All components importing `@/features/tracks/api/trackApi` - Use `tracksApi` instead - **Dependencies**: Action 6.1.1.1 complete - **Risk**: MEDIUM - **Validation**: No direct API imports - **Rollback**: Restore direct imports - [ ] **Action 6.1.1.3**: Create users API service - **Scope**: `apps/web/src/services/api/users.ts` (create) - Export `usersApi` object - **Dependencies**: Action 6.1.1.1 complete - **Risk**: LOW - **Validation**: Service exports user-related methods - **Rollback**: Delete file - [ ] **Action 6.1.1.4**: Create playlists API service - **Scope**: `apps/web/src/services/api/playlists.ts` (create) - Export `playlistsApi` object - **Dependencies**: Action 6.1.1.1 complete - **Risk**: LOW - **Validation**: Service exports playlist-related methods - **Rollback**: Delete file - [ ] **Action 6.1.1.5**: Create auth API service - **Scope**: `apps/web/src/services/api/auth.ts` (create/update) - Export `authApi` object - **Dependencies**: Action 6.1.1.1 complete - **Risk**: LOW - **Validation**: Service exports auth-related methods - **Rollback**: Restore original auth service - [ ] **Action 6.1.1.6**: Replace direct API calls with services (users) - **Scope**: All components importing user API functions - Use `usersApi` instead - **Dependencies**: Action 6.1.1.3 complete - **Risk**: MEDIUM - **Validation**: No direct user API imports - **Rollback**: Restore direct imports - [ ] **Action 6.1.1.7**: Replace direct API calls with services (playlists) - **Scope**: All components importing playlist API functions - Use `playlistsApi` instead - **Dependencies**: Action 6.1.1.4 complete - **Risk**: MEDIUM - **Validation**: No direct playlist API imports - **Rollback**: Restore direct imports - [ ] **Action 6.1.1.8**: Replace direct API calls with services (auth) - **Scope**: All components importing auth API functions - Use `authApi` instead - **Dependencies**: Action 6.1.1.5 complete - **Risk**: MEDIUM - **Validation**: No direct auth API imports - **Rollback**: Restore direct imports - [ ] **Action 6.1.1.9**: Create index file for API services - **Scope**: `apps/web/src/services/api/index.ts` (create/update) - Export all services - **Dependencies**: All services created - **Risk**: LOW - **Validation**: All services exported from index - **Rollback**: Delete index file - [ ] **Action 6.1.1.10**: Update all feature API files to use services - **Scope**: `apps/web/src/features/*/api/*.ts` - Update to use service layer - **Dependencies**: All services created - **Risk**: MEDIUM - **Validation**: Feature APIs use service layer - **Rollback**: Restore direct API calls - [ ] **Action 6.1.1.11**: Remove obsolete feature API files (if any) - **Scope**: Feature API files that are now redundant - Delete if replaced by services - **Dependencies**: Action 6.1.1.10 complete - **Risk**: MEDIUM - **Validation**: Obsolete files deleted - **Rollback**: Restore deleted files ### Sub-Epic 6.2: Code Splitting 🟢 #### Task 6.2.1: Lazy Load Routes - [ ] **Action 6.2.1.1**: Convert routes to lazy loading - **Scope**: Router config - Use `React.lazy()` for all routes - **Dependencies**: None - **Risk**: MEDIUM - **Validation**: Routes load on demand, bundle size reduced - **Rollback**: Remove lazy loading - [ ] **Action 6.2.1.2**: Split vendor bundles - **Scope**: `apps/web/vite.config.ts` - Configure manual chunks - **Dependencies**: None - **Risk**: LOW - **Validation**: Vendor code split into separate chunks - **Rollback**: Remove manual chunks - [ ] **Action 6.2.1.3**: Identify heavy components - **Scope**: Audit all components - List heavy components (charts, editors, large libraries) - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: List of heavy components - **Rollback**: N/A (audit) - [ ] **Action 6.2.1.4**: Dynamic imports for heavy components - **Scope**: Heavy components from Action 6.2.1.3 - Use dynamic imports - **Dependencies**: Action 6.2.1.3 complete - **Risk**: LOW - **Validation**: Heavy components load on demand - **Rollback**: Restore static imports - [ ] **Action 6.2.1.5**: Add loading states for lazy-loaded components - **Scope**: All lazy-loaded components - Add Suspense boundaries with loading states - **Dependencies**: Action 6.2.1.1 complete - **Risk**: LOW - **Validation**: Loading states show during lazy load - **Rollback**: Remove loading states - [ ] **Action 6.2.1.6**: Add error boundaries for lazy-loaded components - **Scope**: All lazy-loaded components - Wrap in error boundaries - **Dependencies**: Action 6.2.1.1 complete, Action 3.3.1.2 complete - **Risk**: LOW - **Validation**: Errors in lazy-loaded components caught - **Rollback**: Remove error boundaries - [ ] **Action 6.2.1.7**: Measure bundle size before/after code splitting - **Scope**: Build process - Measure bundle sizes - **Dependencies**: Action 6.2.1.1 complete - **Risk**: LOW 🔒 - **Validation**: Bundle sizes measured and documented - **Rollback**: N/A (measurement) - [ ] **Action 6.2.1.8**: Optimize bundle sizes if needed - **Scope**: Bundle analysis - Optimize if bundles still too large - **Dependencies**: Action 6.2.1.7 complete - **Risk**: MEDIUM - **Validation**: Bundle sizes optimized - **Rollback**: Restore original bundles ### Sub-Epic 6.3: Virtualization 🟢 #### Task 6.3.1: Implement List Virtualization - [ ] **Action 6.3.1.1**: Install react-window or @tanstack/react-virtual - **Scope**: `apps/web/package.json` - Add dependency - **Dependencies**: None - **Risk**: LOW - **Validation**: Package installed - **Rollback**: Remove from package.json - [ ] **Action 6.3.1.2**: Virtualize LibraryPage track list - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx` - Wrap track list in virtualizer - **Dependencies**: Action 6.3.1.1 complete - **Risk**: MEDIUM - **Validation**: Long lists render smoothly - **Rollback**: Remove virtualization - [ ] **Action 6.3.1.3**: Implement infinite scroll - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx` - Load more on scroll - **Dependencies**: Action 6.3.1.2 complete - **Risk**: MEDIUM - **Validation**: Tracks load as user scrolls - **Rollback**: Restore pagination - [ ] **Action 6.3.1.4**: Add loading indicator for infinite scroll - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx` - Show loading at bottom when fetching more - **Dependencies**: Action 6.3.1.3 complete - **Risk**: LOW - **Validation**: Loading indicator appears when fetching - **Rollback**: Remove indicator - [ ] **Action 6.3.1.5**: Handle infinite scroll edge cases - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx` - Handle end of list, errors, empty states - **Dependencies**: Action 6.3.1.3 complete - **Risk**: MEDIUM - **Validation**: Edge cases handled gracefully - **Rollback**: Remove edge case handling --- ## EPIC 7: VISUAL HIERARCHY 🟢 **Priority**: UX/UI THIRD **Goal**: Establish focal points, reduce visual noise, guide user attention ### Sub-Epic 7.1: Typography System ✅ QUICK WIN #### Task 7.1.1: Define Type Scale - [ ] **Action 7.1.1.1**: Add type scale to design tokens - **Scope**: `apps/web/src/styles/design-tokens.css` - Add `--text-xs` through `--text-4xl` - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: All sizes defined, values match spec - **Rollback**: Remove CSS variables - [ ] **Action 7.1.1.2**: Create Tailwind type utilities - **Scope**: `apps/web/tailwind.config.ts` - Add text size utilities - **Dependencies**: Action 7.1.1.1 complete - **Risk**: LOW 🔒 - **Validation**: `text-xs` through `text-4xl` classes work - **Rollback**: Remove from config - [ ] **Action 7.1.1.3**: Audit all text size classes - **Scope**: Search for `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `text-3xl`, `text-4xl` - List all - **Dependencies**: Action 7.1.1.2 complete - **Risk**: LOW 🔒 - **Validation**: Complete list of text size classes - **Rollback**: N/A (audit) - [ ] **Action 7.1.1.4**: Replace all text-* classes with scale - **Scope**: All components from Action 7.1.1.3 - Replace with scale classes - **Dependencies**: Action 7.1.1.3 complete - **Risk**: MEDIUM (visual changes) - **Validation**: All text uses scale, visual consistency - **Rollback**: Restore original classes - [ ] **Action 7.1.1.5**: Add ESLint rule to enforce type scale - **Scope**: `.eslintrc.js` - Add rule to warn on non-scale text sizes - **Dependencies**: Action 7.1.1.4 complete - **Risk**: LOW 🔒 - **Validation**: ESLint warns on non-scale sizes - **Rollback**: Remove rule #### Task 7.1.2: Fix Heading Inconsistencies - [ ] **Action 7.1.2.1**: Audit all h1 elements - **Scope**: Search for `` elements - Use consistent sizes (text-base for body, text-sm for secondary) - **Dependencies**: Action 7.1.1.3 complete - **Risk**: LOW 🔒 - **Validation**: All paragraphs use type scale - **Rollback**: Restore original sizes ### Sub-Epic 7.2: Spacing System ✅ QUICK WIN #### Task 7.2.1: Define Spacing Scale - [ ] **Action 7.2.1.1**: Add spacing scale to design tokens - **Scope**: `apps/web/src/styles/design-tokens.css` - Add `--spacing-xs` through `--spacing-xxl` - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: All spacing values defined - **Rollback**: Remove CSS variables - [ ] **Action 7.2.1.2**: Audit all spacing classes - **Scope**: Search for `space-y-`, `space-x-`, `gap-`, `p-`, `m-`, `px-`, `py-`, `mx-`, `my-` - List all spacing usage - **Dependencies**: Action 7.2.1.1 complete - **Risk**: LOW 🔒 - **Validation**: Complete list of spacing classes - **Rollback**: N/A (audit) - [ ] **Action 7.2.1.3**: Replace inconsistent spacing - **Scope**: All components from Action 7.2.1.2 - Replace with scale - **Dependencies**: Action 7.2.1.2 complete - **Risk**: MEDIUM (visual changes) - **Validation**: Consistent spacing throughout - **Rollback**: Restore original spacing - [ ] **Action 7.2.1.4**: Add ESLint rule to enforce spacing scale - **Scope**: `.eslintrc.js` - Add rule to warn on non-scale spacing - **Dependencies**: Action 7.2.1.3 complete - **Risk**: LOW 🔒 - **Validation**: ESLint warns on non-scale spacing - **Rollback**: Remove rule - [ ] **Action 7.2.1.3**: Create spacing utility classes - **Scope**: `apps/web/tailwind.config.ts` - Add spacing utilities if needed - **Dependencies**: Action 7.2.1.1 complete - **Risk**: LOW 🔒 - **Validation**: Spacing utilities available - **Rollback**: Remove utilities - [ ] **Action 7.2.1.4**: Document spacing usage - **Scope**: `apps/web/src/styles/design-tokens.css` - Add comments for usage - **Dependencies**: Action 7.2.1.2 complete - **Risk**: LOW 🔒 - **Validation**: Documentation clear - **Rollback**: Remove comments - [ ] **Action 7.2.1.5**: Create SPACING_GUIDE.md - **Scope**: `apps/web/src/styles/SPACING_GUIDE.md` (create) - Document spacing system usage - **Dependencies**: Action 7.2.1.4 complete - **Risk**: LOW 🔒 - **Validation**: Guide documents spacing usage - **Rollback**: Delete file ### Sub-Epic 7.3: Establish Focal Points 🟢 #### Task 7.3.1: Redesign Dashboard Hierarchy - [ ] **Action 7.3.1.1**: Make primary stat (tracks played) large and prominent - **Scope**: `apps/web/src/pages/DashboardPage.tsx:132-164` - Increase size of first stat card - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Primary stat 2x size of others - **Rollback**: Restore equal sizes - [ ] **Action 7.3.1.2**: Reduce welcome message size/weight - **Scope**: `apps/web/src/pages/DashboardPage.tsx:101` - Change `text-4xl` to `text-2xl` - **Dependencies**: Action 7.1.1.3 complete - **Risk**: LOW 🔒 - **Validation**: Message smaller, less prominent - **Rollback**: Restore original size - [ ] **Action 7.3.1.3**: Make Upload button most prominent (FAB or large) - **Scope**: `apps/web/src/pages/DashboardPage.tsx:119-127` - Increase size, add FAB styling - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Upload button most visible - **Rollback**: Restore original styling - [ ] **Action 7.3.1.4**: Collapse activity feed by default - **Scope**: `apps/web/src/pages/DashboardPage.tsx:207-254` - Add collapsible wrapper - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Feed collapsed, expandable - **Rollback**: Remove collapsible - [ ] **Action 7.3.1.5**: Create Collapsible component (if doesn't exist) - **Scope**: `apps/web/src/components/ui/collapsible.tsx` (create) - Reusable collapsible wrapper - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Component works, reusable - **Rollback**: Delete component - [ ] **Action 7.3.1.6**: Use Collapsible for activity feed - **Scope**: `apps/web/src/pages/DashboardPage.tsx:207-254` - Wrap with Collapsible component - **Dependencies**: Action 7.3.1.5 complete - **Risk**: LOW 🔒 - **Validation**: Feed uses Collapsible component - **Rollback**: Remove Collapsible wrapper - [ ] **Action 7.3.1.7**: Add FAB (Floating Action Button) component - **Scope**: `apps/web/src/components/ui/FAB.tsx` (create) - Floating action button component - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: FAB component works - **Rollback**: Delete component - [ ] **Action 7.3.1.8**: Use FAB for Upload button - **Scope**: `apps/web/src/pages/DashboardPage.tsx:119-127` - Replace button with FAB - **Dependencies**: Action 7.3.1.7 complete - **Risk**: LOW 🔒 - **Validation**: Upload uses FAB - **Rollback**: Restore button ### Sub-Epic 7.4: Eliminate Dead Zones 🟢 #### Task 7.4.1: Compact Filters - [ ] **Action 7.4.1.1**: Create Sidebar component (if doesn't exist) - **Scope**: `apps/web/src/components/ui/Sidebar.tsx` (create) - Reusable sidebar component - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Sidebar component works - **Rollback**: Delete component - [ ] **Action 7.4.1.2**: Move filters to sidebar or collapsible section - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx:317-383` - Move to sidebar - **Dependencies**: Action 7.4.1.1 complete - **Risk**: MEDIUM (layout change) - **Validation**: Filters in sidebar, main area for content - **Rollback**: Restore full-width filters - [ ] **Action 7.4.1.3**: Make sidebar collapsible - **Scope**: `apps/web/src/components/ui/Sidebar.tsx` - Add collapse/expand functionality - **Dependencies**: Action 7.4.1.1 complete - **Risk**: LOW 🔒 - **Validation**: Sidebar collapses/expands - **Rollback**: Remove collapse functionality --- ## EPIC 8: INTERACTION CLARITY 🟢 **Priority**: UX/UI THIRD **Goal**: Make actions discoverable, provide feedback, reduce confusion ### Sub-Epic 8.1: Single Action Path 🟢 #### Task 8.1.1: Remove Duplicate Upload Buttons - [ ] **Action 8.1.1.1**: Audit all upload buttons - **Scope**: Search for "Upload" buttons, document locations - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: List of all upload buttons - **Rollback**: N/A (audit) - [ ] **Action 8.1.1.2**: Determine primary upload button location - **Scope**: Based on Action 8.1.1.1 - Decide which location is primary (likely header/FAB) - **Dependencies**: Action 8.1.1.1 complete - **Risk**: LOW - **Validation**: Primary location documented - **Rollback**: N/A (decision) - [ ] **Action 8.1.1.3**: Remove duplicate upload buttons - **Scope**: All locations except primary - Remove duplicate buttons - **Dependencies**: Action 8.1.1.2 complete - **Risk**: MEDIUM (removes functionality) - **Validation**: One upload button, consistent behavior - **Rollback**: Restore duplicates - [ ] **Action 8.1.1.4**: Ensure consistent upload behavior - **Scope**: Primary upload button - Verify same behavior (opens modal/navigates) - **Dependencies**: Action 8.1.1.3 complete - **Risk**: LOW - **Validation**: Upload button behavior consistent - **Rollback**: N/A (verification) ### Sub-Epic 8.2: Visual Affordances 🟢 #### Task 8.2.1: Add Hover States to Interactive Elements - [ ] **Action 8.2.1.1**: Add hover to track cards - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx:413-470` - Add `hover:` classes, `cursor-pointer` - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Cards show hover state, cursor changes - **Rollback**: Remove hover classes - [ ] **Action 8.2.1.2**: Audit all interactive elements - **Scope**: Search for `onClick`, `onMouseEnter`, buttons, links - List all interactive elements - **Dependencies**: Action 8.2.1.1 complete - **Risk**: LOW 🔒 - **Validation**: Complete list of interactive elements - **Rollback**: N/A (audit) - [ ] **Action 8.2.1.3**: Add hover to all clickable elements - **Scope**: All elements from Action 8.2.1.2 - Add hover states and cursor-pointer - **Dependencies**: Action 8.2.1.2 complete - **Risk**: LOW 🔒 - **Validation**: All clickable elements have hover - **Rollback**: Remove hover classes - [ ] **Action 8.2.1.4**: Add focus states for keyboard navigation - **Scope**: All interactive elements - Add focus-visible states - **Dependencies**: Action 8.2.1.3 complete - **Risk**: LOW 🔒 - **Validation**: All elements have focus states - **Rollback**: Remove focus states ### Sub-Epic 8.3: Loading States 🟢 #### Task 8.3.1: Add Loading States to All Mutations - [ ] **Action 8.3.1.1**: Add loading state to addToPlaylist button - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx:137-145` - Use `isLoading` from mutation - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Button shows spinner when loading - **Rollback**: Remove loading state - [ ] **Action 8.3.1.2**: Audit all mutation buttons - **Scope**: Search for mutation calls (`mutateAsync`, `mutate`) - List all mutation buttons - **Dependencies**: Action 8.3.1.1 complete - **Risk**: LOW 🔒 - **Validation**: Complete list of mutation buttons - **Rollback**: N/A (audit) - [ ] **Action 8.3.1.3**: Add loading states to all mutation buttons - **Scope**: All buttons from Action 8.3.1.2 - Add loading states - **Dependencies**: Action 8.3.1.2 complete - **Risk**: LOW 🔒 - **Validation**: All mutations show loading - **Rollback**: Remove loading states - [ ] **Action 8.3.1.4**: Create Spinner component (if doesn't exist) - **Scope**: `apps/web/src/components/ui/Spinner.tsx` (create) - Reusable spinner component - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Spinner component works - **Rollback**: Delete component - [ ] **Action 8.3.1.5**: Use Spinner in loading states - **Scope**: All loading states - Use Spinner component - **Dependencies**: Action 8.3.1.4 complete - **Risk**: LOW 🔒 - **Validation**: All loading states use Spinner - **Rollback**: Remove Spinner usage ### Sub-Epic 8.4: Mode Indicators 🟢 #### Task 8.4.1: Show Bulk Mode Banner - [ ] **Action 8.4.1.1**: Create bulk mode banner component - **Scope**: `apps/web/src/components/BulkModeBanner.tsx` (create) - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Banner shows when bulk mode active - **Rollback**: Delete component - [ ] **Action 8.4.1.2**: Show banner in LibraryPage when bulk mode active - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx` - Add banner, show count - **Dependencies**: Action 8.4.1.1 complete - **Risk**: LOW 🔒 - **Validation**: Banner visible, shows "3 items selected" - **Rollback**: Remove banner - [ ] **Action 8.4.1.3**: Highlight selected items clearly - **Scope**: `apps/web/src/features/library/pages/LibraryPage.tsx:416-419` - Enhance selection styling - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Selected items clearly highlighted - **Rollback**: Restore original styling --- ## EPIC 9: CONSISTENCY 🟢 **Priority**: UX/UI THIRD **Goal**: Establish design system, enforce usage, reduce drift ### Sub-Epic 9.1: Color System 🟢 #### Task 9.1.1: Document Color Usage - [ ] **Action 9.1.1.1**: Create color usage guide - **Scope**: `apps/web/src/styles/COLOR_USAGE.md` (create) - **Dependencies**: None - **Risk**: LOW 🔒 - **Validation**: Guide documents when to use each color - **Rollback**: Delete file - [ ] **Action 9.1.1.2**: Audit Tailwind default color usage - **Scope**: Search for `text-cyan-`, `bg-cyan-`, `border-cyan-` etc. - List all Tailwind default colors - **Dependencies**: Action 9.1.1.1 complete - **Risk**: LOW 🔒 - **Validation**: Complete list of Tailwind default colors - **Rollback**: N/A (audit) - [ ] **Action 9.1.1.3**: Remove Tailwind default colors - **Scope**: All components from Action 9.1.1.2 - Replace with design system colors - **Dependencies**: Action 9.1.1.2 complete - **Risk**: MEDIUM (visual changes) - **Validation**: No Tailwind default colors used - **Rollback**: Restore Tailwind colors - [ ] **Action 9.1.1.4**: Add ESLint rule to prevent Tailwind defaults - **Scope**: `.eslintrc.js` - Add rule to warn on Tailwind default colors - **Dependencies**: Action 9.1.1.3 complete - **Risk**: LOW 🔒 - **Validation**: ESLint warns on Tailwind defaults - **Rollback**: Remove rule ### Sub-Epic 9.2: Component Library 🟢 #### Task 9.2.1: Audit Custom Buttons - [ ] **Action 9.2.1.1**: Find all custom button implementations - **Scope**: Search for `