Fix: Progress listener torn down before background import completes#875
Conversation
The import progress listener was being unsubscribed immediately after importDirectory() returned. However, the backend returns after Phase 1 (parsing) while Phase 2 (database operations - steps 5-9) runs in a background tokio::spawn task. Changes: - Remove automatic listener teardown from importDirectory() - Update app-shell to only unsubscribe when step 9 (complete) is received - Move collection refresh to step 9 handler (after background completes) This ensures users see all 9 progress steps instead of getting stuck on "Creating collections...". Co-Authored-By: Claude <noreply@anthropic.com>
Code Review SummaryVerdict: APPROVE This PR correctly addresses the issue where the status bar was getting stuck on "Creating 30 collections..." during import. The root cause was the progress listener being torn down in Analysis of the FixThe fix is architecturally sound and follows the right pattern:
FindingsNo Critical IssuesThe implementation correctly handles all code paths for listener cleanup. Suggested Improvements[Improvement] If the backend crashes or step 9 is never emitted (e.g., panic in // Optional defensive timeout
const importTimeout = setTimeout(() => {
if (unsubProgress) {
log.warn('Import progress listener timed out - cleaning up');
unsubProgress();
unsubProgress = null;
}
}, 5 * 60 * 1000); // 5 minute timeout
// Clear timeout in step 9 handler and error handlers
clearTimeout(importTimeout);Rationale: Follows defensive programming principle to handle unexpected failure modes, though current implementation is acceptable given the low probability. [Nit] The // Current (line 226):
const { collectionsData } = await import('$lib/stores/collections');
// Could be simplified to just use the existing import:
await collectionsData.loadCollections();However, this is a minor style inconsistency - both work correctly. SecurityNo security concerns - this change only affects UI progress reporting. Testing RecommendationsManual testing as outlined in the PR description is appropriate:
ConclusionThe fix correctly addresses the listener lifecycle issue. The code is well-documented with clear comments explaining the Phase 1/Phase 2 architecture. The suggested timeout improvement is optional and can be added in a follow-up if desired. Reviewed by: Claude Code (Pragmatic Quality Framework) |
malibio
left a comment
There was a problem hiding this comment.
Code review complete - this fix correctly addresses the listener lifecycle issue. Ready for merge pending manual testing of the 9-step import flow.
The collectionsData import is already at the top of the file (line 21), so the dynamic import in the step 9 handler was redundant. Skipped recommendation: Timeout fallback for orphaned listeners - Low probability edge case (backend crash before step 9) - Listener is lightweight, cleaned up on next import or app restart - Adding timeout complexity not justified for marginal benefit Co-Authored-By: Claude <noreply@anthropic.com>
Review Feedback Addressed✅ Addressed (1 recommendation)
⏭️ Skipped (1 recommendation)
Summary
|
…875) * Fix: Progress listener torn down before background import completes The import progress listener was being unsubscribed immediately after importDirectory() returned. However, the backend returns after Phase 1 (parsing) while Phase 2 (database operations - steps 5-9) runs in a background tokio::spawn task. Changes: - Remove automatic listener teardown from importDirectory() - Update app-shell to only unsubscribe when step 9 (complete) is received - Move collection refresh to step 9 handler (after background completes) This ensures users see all 9 progress steps instead of getting stuck on "Creating collections...". Co-Authored-By: Claude <noreply@anthropic.com> * Address review: Remove unnecessary dynamic import The collectionsData import is already at the top of the file (line 21), so the dynamic import in the step 9 handler was redundant. Skipped recommendation: Timeout fallback for orphaned listeners - Low probability edge case (backend crash before step 9) - Listener is lightweight, cleaned up on next import or app restart - Adding timeout complexity not justified for marginal benefit Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
…875) * Fix: Progress listener torn down before background import completes The import progress listener was being unsubscribed immediately after importDirectory() returned. However, the backend returns after Phase 1 (parsing) while Phase 2 (database operations - steps 5-9) runs in a background tokio::spawn task. Changes: - Remove automatic listener teardown from importDirectory() - Update app-shell to only unsubscribe when step 9 (complete) is received - Move collection refresh to step 9 handler (after background completes) This ensures users see all 9 progress steps instead of getting stuck on "Creating collections...". Co-Authored-By: Claude <noreply@anthropic.com> * Address review: Remove unnecessary dynamic import The collectionsData import is already at the top of the file (line 21), so the dynamic import in the step 9 handler was redundant. Skipped recommendation: Timeout fallback for orphaned listeners - Low probability edge case (backend crash before step 9) - Listener is lightweight, cleaned up on next import or app restart - Adding timeout complexity not justified for marginal benefit Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
…875) * Fix: Progress listener torn down before background import completes The import progress listener was being unsubscribed immediately after importDirectory() returned. However, the backend returns after Phase 1 (parsing) while Phase 2 (database operations - steps 5-9) runs in a background tokio::spawn task. Changes: - Remove automatic listener teardown from importDirectory() - Update app-shell to only unsubscribe when step 9 (complete) is received - Move collection refresh to step 9 handler (after background completes) This ensures users see all 9 progress steps instead of getting stuck on "Creating collections...". Co-Authored-By: Claude <noreply@anthropic.com> * Address review: Remove unnecessary dynamic import The collectionsData import is already at the top of the file (line 21), so the dynamic import in the step 9 handler was redundant. Skipped recommendation: Timeout fallback for orphaned listeners - Low probability edge case (backend crash before step 9) - Listener is lightweight, cleaned up on next import or app restart - Adding timeout complexity not justified for marginal benefit Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Problem
The import progress listener was being unsubscribed immediately after
importDirectory()returned. However, the backend returns after Phase 1 (parsing) while Phase 2 (database operations - steps 5-9) runs in a backgroundtokio::spawntask.Changes
importDirectory()Test plan
🤖 Generated with Claude Code