veza/VEZA_MVP_STABILITY_TODOLIST.json

982 lines
No EOL
36 KiB
JSON

{
"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": "todo",
"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": "todo",
"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": "todo",
"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": "todo",
"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": "todo",
"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": "todo",
"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": "todo",
"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": "todo",
"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": 7,
"in_progress": 0,
"todo": 8,
"blocked": 0,
"last_updated": "2025-01-27T20:00:00Z",
"completion_percentage": 47
},
"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"
}
]
}
}