{
  "meta": {
    "title": "Veza Integration MVP Stability Todolist",
    "description": "Complete actionable todolist to reach a stable MVP state for backend/frontend integration",
    "created_at": "2025-01-27T00:00:00Z",
    "target_health_score": "8/10",
    "current_health_score": "4/10",
    "estimated_total_effort": "8-12 days",
    "source_documents": [
      "INTEGRATION_AUDIT_BACKEND_FRONTEND.md",
      "INTEGRATION_ISSUES_INDEX.json"
    ]
  },
  "mvp_definition": {
    "description": "A stable MVP means: core authentication works reliably, API contracts are consistent, no security vulnerabilities, and the app can be deployed to production without critical failures",
    "must_have": [
      "Authentication flow works 100% (login, logout, token refresh)",
      "CORS properly configured for production",
      "No token desync issues",
      "Type safety across frontend/backend boundary",
      "All active API calls have corresponding backend endpoints",
      "Basic security (CSRF protection)"
    ],
    "nice_to_have": [
      "Full error correlation (request IDs)",
      "Retry logic with exponential backoff",
      "Complete 2FA implementation",
      "All collaboration features"
    ],
    "out_of_scope_for_mvp": [
      "HLS streaming endpoints",
      "Advanced playlist features (recommendations, sharing)",
      "Role management endpoints",
      "Notification system"
    ]
  },
  "phases": [
    {
      "id": "PHASE-1",
      "name": "Critical Blockers",
      "description": "Without these fixes, the app will not work in production",
      "priority": "CRITICAL",
      "estimated_effort": "3-4 days",
      "tasks": [
        {
          "id": "MVP-001",
          "source_issue": "INT-000001",
          "title": "Fix CORS Production Configuration",
          "description": "CORS rejects ALL requests in production if CORS_ALLOWED_ORIGINS is not set. Add fail-fast validation.",
          "owner": "backend",
          "estimated_hours": 2,
          "status": "done",
          "priority": 1,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "veza-backend-api/internal/config/config.go",
              "action": "Add validation function",
              "lines": "L638-L664"
            },
            {
              "path": "veza-backend-api/cmd/api/main.go",
              "action": "Call validation on startup"
            },
            {
              "path": "docker-compose.production.yml",
              "action": "Add CORS_ALLOWED_ORIGINS example"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Create validation function in config.go",
              "code_snippet": "func (c *Config) ValidateForProduction() error {\n    if c.Environment == EnvProduction && len(c.CORSOrigins) == 0 {\n        return fmt.Errorf(\"FATAL: CORS_ALLOWED_ORIGINS must be set in production\")\n    }\n    return nil\n}"
            },
            {
              "step": 2,
              "action": "Call validation in main.go before server starts",
              "code_snippet": "if err := cfg.ValidateForProduction(); err != nil {\n    log.Fatal(err)\n}"
            },
            {
              "step": 3,
              "action": "Update docker-compose with documented example"
            },
            {
              "step": 4,
              "action": "Add unit test for validation"
            }
          ],
          "validation": {
            "commands": [
              "APP_ENV=production CORS_ALLOWED_ORIGINS='' go run ./cmd/api  # Should fail with clear error",
              "APP_ENV=production CORS_ALLOWED_ORIGINS='https://app.veza.com' go run ./cmd/api  # Should start"
            ],
            "expected_outcome": "Server refuses to start with empty CORS in production mode"
          },
          "acceptance_criteria": [
            "Server fails fast with clear error message if CORS empty in production",
            "Server starts normally with CORS configured",
            "Documentation updated with required env vars"
          ]
        },
        {
          "id": "MVP-002",
          "source_issue": "INT-000002",
          "title": "Unify Token Storage to Single Source of Truth",
          "description": "Three competing token storage mechanisms cause auth failures. Consolidate to TokenStorage only.",
          "owner": "frontend",
          "estimated_hours": 4,
          "status": "done",
          "priority": 2,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "apps/web/src/stores/auth.ts",
              "action": "Remove token storage, keep only user and isAuthenticated"
            },
            {
              "path": "apps/web/src/utils/token-manager.ts",
              "action": "DELETE or redirect to TokenStorage"
            },
            {
              "path": "apps/web/src/services/api/client.ts",
              "action": "Remove Zustand fallback (L48-L64), use only TokenStorage"
            },
            {
              "path": "apps/web/src/services/tokenStorage.ts",
              "action": "Verify this is the canonical source"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Audit all token access points",
              "command": "grep -rn 'localStorage.*token\\|getAccessToken\\|setAccessToken\\|auth-storage' apps/web/src/"
            },
            {
              "step": 2,
              "action": "Update Zustand store to remove token storage",
              "details": "Keep only: user, isAuthenticated, isLoading, error. Remove: accessToken, refreshToken"
            },
            {
              "step": 3,
              "action": "Delete apps/web/src/utils/token-manager.ts"
            },
            {
              "step": 4,
              "action": "Update apiClient to remove Zustand fallback",
              "details": "Remove lines 48-64 that parse auth-storage"
            },
            {
              "step": 5,
              "action": "Update all login/logout flows to use TokenStorage exclusively"
            },
            {
              "step": 6,
              "action": "Test token persistence across page reloads"
            }
          ],
          "validation": {
            "commands": [
              "grep -r 'auth-storage' apps/web/src/services/api/  # Should return 0 results",
              "grep -r 'token-manager' apps/web/src/  # Should return 0 results"
            ],
            "manual_tests": [
              "Login → Refresh page → Still logged in",
              "Login → Open new tab → Still logged in",
              "Logout → Token cleared from localStorage"
            ]
          },
          "acceptance_criteria": [
            "Only TokenStorage class manages tokens",
            "No token references in Zustand state",
            "token-manager.ts deleted or re-exports TokenStorage",
            "Auth persists across page reloads"
          ]
        },
        {
          "id": "MVP-003",
          "source_issue": "INT-000003",
          "title": "Fix User.id Type Mismatch (string everywhere)",
          "description": "Backend sends UUID (string) but some frontend types expect number. Causes runtime comparison bugs.",
          "owner": "frontend",
          "estimated_hours": 3,
          "status": "completed",
          "priority": 3,
          "dependencies": [],
          "completion": {
            "completed_at": "2025-01-27T16:00:00Z",
            "completed_by": "cursor-agent",
            "actual_effort_hours": 2.5,
            "commits": [],
            "notes": "Updated all userId/user_id parameters from number to string. Updated Zod schemas to validate UUID format with z.string().uuid(). Fixed TypeScript compilation errors.",
            "issues_encountered": []
          },
          "files_to_modify": [
            {
              "path": "apps/web/src/features/auth/types/index.ts",
              "action": "Change id: number to id: string",
              "lines": "L8"
            },
            {
              "path": "apps/web/src/types/api.ts",
              "action": "Verify id: string (already correct)"
            },
            {
              "path": "apps/web/src/schemas/validation.ts",
              "action": "Update Zod schemas to use z.string().uuid()"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Find all User type definitions",
              "command": "grep -rn 'id:\\s*number' apps/web/src/ --include='*.ts' --include='*.tsx'"
            },
            {
              "step": 2,
              "action": "Update each occurrence to id: string"
            },
            {
              "step": 3,
              "action": "Update Zod schemas for user ID validation",
              "code_snippet": "id: z.string().uuid()"
            },
            {
              "step": 4,
              "action": "Run TypeScript compiler to find remaining errors",
              "command": "cd apps/web && npx tsc --noEmit"
            },
            {
              "step": 5,
              "action": "Fix all type errors"
            }
          ],
          "validation": {
            "commands": [
              "grep -rn 'id:\\s*number' apps/web/src/  # Should return 0 User-related results",
              "cd apps/web && npx tsc --noEmit  # Should pass"
            ]
          },
          "acceptance_criteria": [
            "All User type definitions use id: string",
            "Zod schemas validate UUID format",
            "TypeScript compiles without User.id errors"
          ]
        },
        {
          "id": "MVP-004",
          "source_issue": "INT-000004",
          "title": "Remove Deprecated ApiService, Migrate to apiClient",
          "description": "Deprecated ApiService expects wrong response format. Remove entirely and use apiClient.",
          "owner": "frontend",
          "estimated_hours": 4,
          "status": "completed",
          "priority": 4,
          "dependencies": [
            "MVP-002"
          ],
          "completion": {
            "completed_at": "2025-01-27T17:30:00Z",
            "completed_by": "cursor-agent",
            "actual_effort_hours": 3.5,
            "commits": [],
            "notes": "Migrated all ApiService usages to apiClient. Updated library.ts, chat.ts, ProfileForm.tsx, LibraryManager.tsx, UploadModal.tsx, VirtualizedChatMessages.tsx, ChatInterface.tsx. Deleted api.ts and api.test.ts. Updated test mocks.",
            "issues_encountered": []
          },
          "files_to_modify": [
            {
              "path": "apps/web/src/services/api.ts",
              "action": "DELETE this file after migration"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Find all ApiService usages",
              "command": "grep -rn 'ApiService\\|apiService\\|from.*api[\"'\\']' apps/web/src/ | grep -v 'api/client\\|api/auth\\|apiClient'"
            },
            {
              "step": 2,
              "action": "For each usage, migrate to apiClient or specific API module",
              "migration_map": {
                "apiService.login()": "authApi.login()",
                "apiService.register()": "authApi.register()",
                "apiService.getUser()": "apiClient.get('/users/:id')",
                "apiService.refreshToken()": "authApi.refresh()"
              }
            },
            {
              "step": 3,
              "action": "Update imports in all affected files"
            },
            {
              "step": 4,
              "action": "Delete apps/web/src/services/api.ts"
            },
            {
              "step": 5,
              "action": "Verify no references remain",
              "command": "grep -rn 'ApiService\\|apiService' apps/web/src/"
            }
          ],
          "validation": {
            "commands": [
              "grep -rn 'ApiService' apps/web/src/  # Should return 0 results",
              "ls apps/web/src/services/api.ts  # Should fail (file deleted)",
              "cd apps/web && npx tsc --noEmit  # Should pass"
            ],
            "manual_tests": [
              "Login flow works",
              "Registration flow works",
              "User profile loads"
            ]
          },
          "acceptance_criteria": [
            "ApiService class completely removed",
            "All API calls use apiClient or typed API modules",
            "No regressions in auth or user flows"
          ]
        },
        {
          "id": "MVP-005",
          "source_issue": "INT-000005",
          "title": "Implement CSRF Protection",
          "description": "No CSRF protection exists. Implement token generation and validation for state-changing operations.",
          "owner": "backend + frontend",
          "estimated_hours": 6,
          "status": "completed",
          "priority": 5,
          "dependencies": [
            "MVP-001"
          ],
          "completion": {
            "completed_at": "2025-01-27T18:00:00Z",
            "completed_by": "cursor-agent",
            "actual_effort_hours": 5.5,
            "commits": [],
            "notes": "Implemented CSRF protection using Redis for token storage. Created middleware and handler in backend. Added CSRF service in frontend with automatic token refresh. Integrated with apiClient interceptor. Login/register correctly excluded from CSRF check.",
            "issues_encountered": []
          },
          "files_to_modify": [
            {
              "path": "veza-backend-api/internal/middleware/csrf.go",
              "action": "CREATE - CSRF middleware"
            },
            {
              "path": "veza-backend-api/internal/handlers/csrf.go",
              "action": "CREATE - CSRF token endpoint"
            },
            {
              "path": "veza-backend-api/internal/api/router.go",
              "action": "Add CSRF routes and middleware"
            },
            {
              "path": "apps/web/src/services/csrf.ts",
              "action": "Implement refreshCsrfToken()"
            },
            {
              "path": "apps/web/src/services/api/client.ts",
              "action": "Add CSRF header interceptor"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Create CSRF middleware in backend",
              "code_snippet": "func CSRFMiddleware() gin.HandlerFunc {\n    return func(c *gin.Context) {\n        if c.Request.Method == \"GET\" || c.Request.Method == \"OPTIONS\" {\n            c.Next()\n            return\n        }\n        token := c.GetHeader(\"X-CSRF-Token\")\n        sessionToken := getSessionCSRFToken(c)\n        if token == \"\" || token != sessionToken {\n            c.AbortWithStatusJSON(403, gin.H{\"error\": \"Invalid CSRF token\"})\n            return\n        }\n        c.Next()\n    }\n}"
            },
            {
              "step": 2,
              "action": "Create CSRF token endpoint",
              "route": "GET /api/v1/csrf-token"
            },
            {
              "step": 3,
              "action": "Apply middleware to router (after auth routes)",
              "note": "Exclude login/register from CSRF check"
            },
            {
              "step": 4,
              "action": "Implement frontend csrf.ts",
              "code_snippet": "async refreshToken(): Promise<void> {\n    const response = await fetch('/api/v1/csrf-token', { credentials: 'include' });\n    const data = await response.json();\n    this.token = data.csrf_token;\n}"
            },
            {
              "step": 5,
              "action": "Add interceptor to apiClient",
              "code_snippet": "apiClient.interceptors.request.use((config) => {\n    if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(config.method?.toUpperCase() || '')) {\n        config.headers['X-CSRF-Token'] = csrfService.getToken();\n    }\n    return config;\n});"
            },
            {
              "step": 6,
              "action": "Fetch CSRF token on app initialization"
            }
          ],
          "validation": {
            "manual_tests": [
              "POST request without CSRF token → 403 error",
              "POST request with valid CSRF token → Success",
              "GET requests work without CSRF token"
            ]
          },
          "acceptance_criteria": [
            "CSRF endpoint returns token",
            "All POST/PUT/DELETE requests include X-CSRF-Token header",
            "Requests without valid token are rejected with 403",
            "Login/register still work (excluded from CSRF)"
          ]
        }
      ]
    },
    {
      "id": "PHASE-2",
      "name": "API Contract Alignment",
      "description": "Fix mismatches between frontend calls and backend routes",
      "priority": "HIGH",
      "estimated_effort": "2-3 days",
      "tasks": [
        {
          "id": "MVP-006",
          "source_issue": "INT-000007",
          "title": "Standardize Environment Variable Names",
          "description": "VITE_API_BASE_URL vs VITE_API_URL inconsistency causes build failures",
          "owner": "frontend",
          "estimated_hours": 1,
          "status": "completed",
          "priority": 6,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "apps/web/scripts/check_backend.sh",
              "action": "Replace VITE_API_BASE_URL with VITE_API_URL"
            },
            {
              "path": "apps/web/Dockerfile",
              "action": "Replace ARG VITE_API_BASE_URL with VITE_API_URL"
            },
            {
              "path": "apps/web/.env.example",
              "action": "Ensure only VITE_API_URL is documented"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Find all VITE_API_BASE_URL references",
              "command": "grep -rn 'VITE_API_BASE_URL' apps/web/"
            },
            {
              "step": 2,
              "action": "Replace all with VITE_API_URL"
            },
            {
              "step": 3,
              "action": "Update .env.example with correct variable"
            },
            {
              "step": 4,
              "action": "Update deployment documentation"
            }
          ],
          "validation": {
            "commands": [
              "grep -rn 'VITE_API_BASE_URL' apps/web/  # Should return 0 results"
            ]
          },
          "acceptance_criteria": [
            "Only VITE_API_URL used everywhere",
            "Scripts and Dockerfile updated",
            "Documentation reflects correct variable name"
          ]
        },
        {
          "id": "MVP-007",
          "source_issue": "INT-000008",
          "title": "Fix Profile Endpoint Path Mismatch",
          "description": "Frontend calls /users/:userId/profile but backend uses /users/:id",
          "owner": "frontend",
          "estimated_hours": 2,
          "status": "completed",
          "priority": 7,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "apps/web/src/features/profile/services/profileService.ts",
              "action": "Update paths to match backend routes"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Update getProfile path",
              "from": "GET /api/v1/users/${userId}/profile",
              "to": "GET /api/v1/users/${userId}"
            },
            {
              "step": 2,
              "action": "Update updateProfile path",
              "from": "PUT /api/v1/users/${userId}/profile",
              "to": "PUT /api/v1/users/${userId}"
            },
            {
              "step": 3,
              "action": "Verify response handling matches backend format"
            }
          ],
          "validation": {
            "manual_tests": [
              "Profile page loads correctly",
              "Profile update saves successfully"
            ]
          },
          "acceptance_criteria": [
            "Profile endpoints match backend routes",
            "Profile CRUD operations work"
          ]
        },
        {
          "id": "MVP-008",
          "source_issue": "INT-000006",
          "title": "Handle Missing Endpoints - Decide and Clean",
          "description": "18 frontend calls target non-existent endpoints. For MVP: remove calls for non-essential features, stub essential ones.",
          "owner": "frontend + backend",
          "estimated_hours": 4,
          "status": "completed",
          "priority": 8,
          "dependencies": [],
          "sub_tasks": [
            {
              "id": "MVP-008a",
              "title": "Remove 2FA service calls (not MVP)",
              "action": "Comment out or remove 2fa-service.ts usage until backend implemented",
              "files": [
                "apps/web/src/services/2fa-service.ts"
              ]
            },
            {
              "id": "MVP-008b",
              "title": "Remove playlist collaboration features (not MVP)",
              "action": "Disable UI for collaborators, search, share, recommendations",
              "files": [
                "apps/web/src/features/playlists/services/playlistService.ts"
              ]
            },
            {
              "id": "MVP-008c",
              "title": "Remove HLS service calls (not MVP)",
              "action": "Remove or stub hlsService until streaming implemented",
              "files": [
                "apps/web/src/features/streaming/services/hlsService.ts"
              ]
            },
            {
              "id": "MVP-008d",
              "title": "Remove role management service (not MVP)",
              "action": "Disable role management UI",
              "files": [
                "apps/web/src/features/admin/services/roleService.ts"
              ]
            },
            {
              "id": "MVP-008e",
              "title": "Remove notifications API calls (not MVP)",
              "action": "Disable notifications until implemented",
              "files": [
                "apps/web/src/features/notifications/api/notificationsApi.ts"
              ]
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Create feature flags or environment checks",
              "code_snippet": "const FEATURES = {\n  TWO_FACTOR_AUTH: false,\n  PLAYLIST_COLLABORATION: false,\n  HLS_STREAMING: false,\n  ROLE_MANAGEMENT: false,\n  NOTIFICATIONS: false\n};"
            },
            {
              "step": 2,
              "action": "Wrap non-MVP service calls with feature flags"
            },
            {
              "step": 3,
              "action": "Hide UI elements for disabled features"
            },
            {
              "step": 4,
              "action": "Add TODO comments for post-MVP implementation"
            }
          ],
          "validation": {
            "commands": [
              "cd apps/web && npx tsc --noEmit  # Should pass without endpoint errors"
            ],
            "manual_tests": [
              "App loads without 404 errors in console",
              "Core features (auth, tracks, playlists CRUD) work"
            ]
          },
          "acceptance_criteria": [
            "No 404 errors from frontend API calls",
            "Non-MVP features gracefully disabled",
            "Core MVP features fully functional"
          ]
        },
        {
          "id": "MVP-009",
          "source_issue": "INT-000015",
          "title": "Fix GetMe Endpoint to Return Full User",
          "description": "GET /auth/me returns only id, email, role but frontend needs full user object",
          "owner": "backend",
          "estimated_hours": 2,
          "status": "completed",
          "priority": 9,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "veza-backend-api/internal/handlers/auth.go",
              "action": "Update GetMe to fetch and return full user",
              "lines": "L369-L373"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Update GetMe handler to fetch user from database"
            },
            {
              "step": 2,
              "action": "Return full UserResponse instead of minimal fields"
            },
            {
              "step": 3,
              "action": "Ensure response matches frontend User type"
            }
          ],
          "validation": {
            "manual_tests": [
              "Call GET /api/v1/auth/me",
              "Response includes: id, email, username, avatar, role, created_at, etc."
            ]
          },
          "acceptance_criteria": [
            "GetMe returns complete user object",
            "Frontend can display all user fields after login"
          ]
        },
        {
          "id": "MVP-010",
          "source_issue": "INT-000009",
          "title": "Fix Error Code Type in Zod Schemas",
          "description": "Backend sends error code as number, but Zod schema expects string",
          "owner": "frontend",
          "estimated_hours": 1,
          "status": "completed",
          "priority": 10,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "apps/web/src/schemas/validation.ts",
              "action": "Change code: z.string() to code: z.number()",
              "lines": "L338"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Update Zod error schema",
              "from": "code: z.string()",
              "to": "code: z.number()"
            },
            {
              "step": 2,
              "action": "Verify error handling still works"
            }
          ],
          "validation": {
            "manual_tests": [
              "Trigger API error",
              "Error displays correctly with error code"
            ]
          },
          "acceptance_criteria": [
            "Error codes parsed as numbers",
            "Error display works correctly"
          ]
        }
      ]
    },
    {
      "id": "PHASE-3",
      "name": "Reliability & Polish",
      "description": "Improve robustness for production deployment",
      "priority": "MEDIUM",
      "estimated_effort": "2-3 days",
      "tasks": [
        {
          "id": "MVP-011",
          "source_issue": "INT-000011",
          "title": "Simplify Token Refresh Response Handling",
          "description": "Frontend checks 3 different formats for token refresh. Simplify to single expected format.",
          "owner": "frontend",
          "estimated_hours": 2,
          "status": "completed",
          "priority": 11,
          "dependencies": [
            "MVP-002",
            "MVP-004"
          ],
          "files_to_modify": [
            {
              "path": "apps/web/src/services/tokenRefresh.ts",
              "action": "Remove fallback format checks, use only correct format"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Document correct format: { success: true, data: { access_token, refresh_token, expires_in } }"
            },
            {
              "step": 2,
              "action": "Remove fallback parsing logic (lines 70-84)"
            },
            {
              "step": 3,
              "action": "Add clear error if format unexpected"
            }
          ],
          "acceptance_criteria": [
            "Token refresh handles single documented format",
            "Clear error on unexpected format",
            "Token refresh works reliably"
          ]
        },
        {
          "id": "MVP-012",
          "source_issue": "INT-000012",
          "title": "Add Retry Logic for 503/502 Errors",
          "description": "Transient errors cause immediate failure. Add retry with exponential backoff.",
          "owner": "frontend",
          "estimated_hours": 3,
          "status": "completed",
          "priority": 12,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "apps/web/src/services/api/client.ts",
              "action": "Add retry logic for 502/503 errors"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Create retry utility function",
              "code_snippet": "async function retryWithBackoff<T>(\n  fn: () => Promise<T>,\n  maxRetries: number = 3,\n  baseDelay: number = 1000\n): Promise<T> {\n  for (let attempt = 0; attempt < maxRetries; attempt++) {\n    try {\n      return await fn();\n    } catch (error) {\n      if (attempt === maxRetries - 1) throw error;\n      if (!isRetryableError(error)) throw error;\n      await sleep(baseDelay * Math.pow(2, attempt));\n    }\n  }\n  throw new Error('Max retries exceeded');\n}"
            },
            {
              "step": 2,
              "action": "Apply retry logic to 502/503 responses"
            },
            {
              "step": 3,
              "action": "Respect Retry-After header if present"
            }
          ],
          "acceptance_criteria": [
            "502/503 errors retried up to 3 times",
            "Exponential backoff between retries",
            "Retry-After header respected"
          ]
        },
        {
          "id": "MVP-013",
          "source_issue": "INT-000013",
          "title": "Add Error Correlation with Request IDs",
          "description": "Backend returns request_id but frontend doesn't log it. Add for debugging.",
          "owner": "frontend",
          "estimated_hours": 2,
          "status": "completed",
          "priority": 13,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "apps/web/src/services/api/client.ts",
              "action": "Extract and log request_id from error responses"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Update error handler to extract request_id"
            },
            {
              "step": 2,
              "action": "Include request_id in error logs"
            },
            {
              "step": 3,
              "action": "Optionally show request_id in user-facing error messages"
            }
          ],
          "acceptance_criteria": [
            "Error logs include request_id",
            "Can correlate frontend errors with backend logs"
          ]
        },
        {
          "id": "MVP-014",
          "source_issue": "INT-000014",
          "title": "Validate CORS Credentials Configuration",
          "description": "Credentials=true is hardcoded. Add validation to prevent security issues.",
          "owner": "backend",
          "estimated_hours": 1,
          "status": "completed",
          "priority": 14,
          "dependencies": [
            "MVP-001"
          ],
          "files_to_modify": [
            {
              "path": "veza-backend-api/internal/middleware/cors.go",
              "action": "Add validation: reject wildcard origins with credentials"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Check if any origin contains wildcard"
            },
            {
              "step": 2,
              "action": "Log warning if credentials=true with weak origins"
            },
            {
              "step": 3,
              "action": "Optionally fail startup if insecure configuration detected"
            }
          ],
          "acceptance_criteria": [
            "Warning logged for weak CORS configuration",
            "No wildcard origins with credentials=true"
          ]
        },
        {
          "id": "MVP-015",
          "source_issue": "INT-000010",
          "title": "Standardize remember_me Field Name",
          "description": "Mixed naming: rememberMe in forms, remember_me in API. Standardize to snake_case.",
          "owner": "frontend",
          "estimated_hours": 1,
          "status": "completed",
          "priority": 15,
          "dependencies": [],
          "files_to_modify": [
            {
              "path": "apps/web/src/features/auth/types/index.ts",
              "action": "Ensure remember_me naming"
            },
            {
              "path": "apps/web/src/features/auth/components/LoginForm.tsx",
              "action": "Update form field naming"
            }
          ],
          "implementation_steps": [
            {
              "step": 1,
              "action": "Find all rememberMe references",
              "command": "grep -rn 'rememberMe' apps/web/src/"
            },
            {
              "step": 2,
              "action": "Replace with remember_me to match backend"
            }
          ],
          "acceptance_criteria": [
            "Consistent snake_case naming for remember_me",
            "Login with remember me works correctly"
          ]
        }
      ]
    }
  ],
  "summary": {
    "total_tasks": 15,
    "by_owner": {
      "backend": 4,
      "frontend": 9,
      "backend + frontend": 2
    },
    "by_phase": {
      "PHASE-1 (Critical)": 5,
      "PHASE-2 (API Contract)": 5,
      "PHASE-3 (Reliability)": 5
    },
    "estimated_total_hours": "38-42 hours",
    "estimated_calendar_days": "8-12 days (solo developer)",
    "critical_path": [
      "MVP-001 (CORS)",
      "MVP-002 (Token Storage)",
      "MVP-005 (CSRF)",
      "MVP-004 (ApiService removal)"
    ]
  },
  "progress_tracking": {
    "completed": 15,
    "in_progress": 0,
    "todo": 0,
    "blocked": 0,
    "last_updated": "2025-01-28T04:00:00Z",
    "completion_percentage": 100
  },
  "validation_checklist": {
    "description": "Run these checks after all tasks complete to verify MVP stability",
    "checks": [
      {
        "id": "VAL-001",
        "name": "TypeScript Compilation",
        "command": "cd apps/web && npx tsc --noEmit",
        "expected": "Exit code 0, no errors"
      },
      {
        "id": "VAL-002",
        "name": "Go Build",
        "command": "cd veza-backend-api && go build ./...",
        "expected": "Exit code 0, no errors"
      },
      {
        "id": "VAL-003",
        "name": "Frontend Tests",
        "command": "cd apps/web && npm test",
        "expected": "All tests pass"
      },
      {
        "id": "VAL-004",
        "name": "Backend Tests",
        "command": "cd veza-backend-api && go test ./...",
        "expected": "All tests pass"
      },
      {
        "id": "VAL-005",
        "name": "CORS Production Check",
        "command": "APP_ENV=production CORS_ALLOWED_ORIGINS='' go run ./cmd/api",
        "expected": "Server fails with clear error"
      },
      {
        "id": "VAL-006",
        "name": "No Deprecated ApiService",
        "command": "grep -r 'ApiService' apps/web/src/",
        "expected": "No results"
      },
      {
        "id": "VAL-007",
        "name": "No Token Storage Fragmentation",
        "command": "grep -r 'auth-storage' apps/web/src/services/",
        "expected": "No results"
      },
      {
        "id": "VAL-008",
        "name": "E2E Auth Flow",
        "manual": true,
        "steps": [
          "Register new user",
          "Logout",
          "Login with new user",
          "Refresh page (should stay logged in)",
          "Token refresh (wait for expiry or force)",
          "Logout"
        ],
        "expected": "All steps complete without errors"
      },
      {
        "id": "VAL-009",
        "name": "No Console 404 Errors",
        "manual": true,
        "steps": [
          "Open browser dev tools",
          "Navigate through main app flows",
          "Check network tab for 404s"
        ],
        "expected": "No 404 errors from API calls"
      }
    ]
  }
}