You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: prevent duplicate task generation button for late-joining users (#228)
* fix(ui): prevent duplicate task generation button for late-joining users
This addresses a UX issue where users who log into a project where
tasks have already been generated still see the "Generate Task Breakdown"
button, which fails with a 400 error when clicked.
Changes:
- Backend: Make generate-tasks endpoint idempotent by returning
{success: true, tasks_already_exist: true} instead of 400 error
when tasks already exist
- Frontend: Check for existing tasks on component mount during
state initialization (in fetchProgress with initializePrdState)
- Frontend: Handle idempotent backend response gracefully in
handleGenerateTaskBreakdown
- Types: Add optional tasks_already_exist field to API response type
This implements defensive programming on both sides:
1. Frontend checks task existence on mount (covers late-joining users)
2. Backend handles duplicate requests gracefully (covers edge cases)
3. Error handler transitions to "tasks ready" state on idempotent response
TDD: Tests written first, all 95 frontend + 9 backend tests passing.
* test(e2e): add late-joining user tests for task generation
Addresses a critical E2E test coverage gap: no tests verified UI state
for users who arrive AFTER WebSocket events have occurred.
Changes:
- Add test_late_joining_user.spec.ts with scenarios:
- Tasks already exist → should see "Review Tasks" not "Generate Tasks"
- PRD already complete → should see "View PRD" with correct state
- Idempotent API call → backend returns success, not error
- Update seed-test-data.py to create "planning" phase project:
- Set project phase to "planning" instead of "discovery"
- Set discovery state to "completed"
- Seed PRD content in memory table
Why this gap existed:
- All existing E2E tests followed real-time happy path flow
- Tests assumed user was present during WebSocket events
- Conditional skip patterns (test.skip when element not visible)
masked the issue instead of catching it
This ensures future similar bugs are caught by E2E tests.
* fix(e2e): correct projects table schema in seed script
The seed script was using incorrect columns (slug, updated_at) that
don't exist in the projects table. Updated to use the correct schema:
id, name, description, user_id, workspace_path, phase, created_at
This enables proper seeding of Project 2 for late-joining user tests.
* fix(e2e): complete Project 2 seed data for late-joining user tests
The dashboard was crashing for Project 2 due to missing required fields:
- Added `status` field (was null, causing .toUpperCase() error)
- Added workspace directory creation
- Added project-agent assignments
- Fixed tasks table columns (assigned_to not assigned_agent)
Also updated debug-error.spec.ts to use E2E_TEST_PROJECT_PLANNING_ID
for testing Project 2 scenarios.
* fix: address code review feedback for late-joining user feature
- Fix agent ID mismatch: backend-001 → backend-worker-001, frontend-001 → frontend-specialist-001
- Add taskStateInitialized flag to prevent race condition during async task check
- Add user notification when tasks already exist (tasksAlreadyExistMessage)
- Simplify E2E test assertions with explicit expect() calls instead of conditionals
- API type already had tasks_already_exist field (verified, no change needed)
* test(e2e): add curated smoke test suite with @smoke tagging
- Update test:smoke script to filter by @smoke tag (Chromium only)
- Tag 4 critical-path tests as @smoke:
- Auth: login with valid credentials
- Project: create new project via UI
- Dashboard: display all main sections
- Late-joining: show Tasks Ready when tasks exist
- Remove WebSocket test from smoke suite (flaky, needs investigation)
- Smoke suite runs in ~45s for fast CI feedback
* fix(websocket): queue subscriptions when socket not yet open
Root cause: subscribe() silently did nothing when called before the
WebSocket was OPEN. AgentStateProvider calls connect() then immediately
subscribe(), but the socket is still CONNECTING, causing the subscribe
message to never be sent.
Fix: Add pendingSubscriptions queue that holds project IDs until the
socket opens, then sends all queued subscriptions in onopen handler.
This fixes the WebSocket E2E test regression and re-enables it as a
smoke test.
* fix(e2e): align tasks_p2 schema with 22-column tasks table format
Update Project 2 task seeding to use the full tasks table schema:
- Add all 22 columns (issue_id, task_number, depends_on, etc.)
- Include realistic values for tokens, quality gates, timestamps
- Match the format used for Project 1 tasks
This ensures consistency between projects and prevents potential
schema mismatches that could cause test failures.
* fix(ux): convert tasks-already-exist notification to fixed-position toast
Convert the inline notification for "tasks already exist" (idempotent
response) to a fixed-position toast notification in the bottom-right
corner. This ensures the feedback is always visible regardless of
scroll position.
- Moved notification outside main content flow
- Added fixed positioning (bottom-4 right-4)
- Added shadow and slide-in animation
- Maintains accessibility attributes (role="status", aria-live="polite")
* test: add missing API mocks for task-generation-section tests
The tests expecting task-generation-section to appear were missing
mocks for mockGetPRD and mockTasksList. These are required because
the component now waits for taskStateInitialized before showing the
"Generate Task Breakdown" button (prevents button flash on mount).
* fix(e2e): correct project 2 seeding schema alignment
Two fixes for seed-test-data.py:
1. Add missing is_active column to project 2 agent assignments
- Schema requires 5 columns: project_id, agent_id, role, is_active, assigned_at
- Was using 4 columns, causing NOT NULL constraint errors
2. Add table_exists guard for project 2 memory table inserts
- Discovery state and PRD content inserts now check TABLE_MEMORY exists
- Matches defensive pattern used for project 1
* fix(react): prevent state update on unmounted component
Replace inline setTimeout in handleGenerateTaskBreakdown with a useEffect
that properly manages the timeout lifecycle. This prevents the React warning
"Can't perform a React state update on an unmounted component" if the
component unmounts before the 3-second timeout completes.
The useEffect:
- Watches tasksAlreadyExistMessage state
- Sets timeout only when message is true
- Returns cleanup function that clears the timeout on unmount
0 commit comments