Skip to content

Fix control message loop and simplify welcome screen repository display #372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 16, 2025

Conversation

steipete
Copy link
Collaborator

Summary

  • Fixed infinite loop in control message handling for repository-path-update responses
  • Simplified the welcome screen to show repository count instead of a full list

Changes

Control Message Loop Fix

The server was getting into an infinite loop when processing repository-path-update messages:

  1. Server receives a repository-path-update message
  2. It processes it but finds "Missing path in payload"
  3. It sends back an error response
  4. The error response was being processed again by the handler, creating a new error response
  5. This created an infinite loop

Fix: Added a check to skip processing response messages that don't have pending request handlers. This prevents error responses from being re-processed.

Welcome Screen UI Improvement

The Project Folder page in the welcome flow had limited space but was trying to show a scrollable list of all discovered repositories.

Fix: Replaced the ScrollView list with a simple count display:

  • Shows "Scanning..." while searching for repositories
  • Shows "No repositories found" when none exist
  • Shows "X repositories found" with proper singular/plural handling
  • Much cleaner and more compact UI that fits better in the welcome flow

Test Plan

  • Verified control message handling no longer loops
  • Tested welcome screen shows correct repository counts
  • Verified scanning animation works correctly
  • Checked singular/plural text handling

steipete added 3 commits July 16, 2025 03:42
Based on user feedback, document the correct flow for pasting on mobile devices:
1. Press paste button on keyboard toolbar first
2. White input box appears as workaround for browser restrictions
3. Long-press in box to access native paste menu

This clarifies the non-obvious interaction pattern for mobile users.
The issue was that response messages with errors were being processed by handlers again, creating an infinite loop. Now response messages that aren't pending requests are properly ignored to prevent this loop.

The fix:
- Skip all response messages that don't have pending request handlers
- This prevents error responses from being re-processed by the system handler
- Maintains proper handling of expected responses via pendingRequests map
Instead of listing all discovered repositories in a scrollable view, now just show the count. This provides a cleaner UI when there's limited space in the welcome screen.

Changes:
- Replace ScrollView list with a simple HStack showing repository count
- Show appropriate messages for scanning, no repos found, or count
- Remove the 10-repository limit since we're only showing count now
- Cleaner, more compact UI that fits better in the welcome flow
Copy link

claude bot commented Jul 16, 2025

Claude review from 7/16/2025, 1:55:35 AM (outdated - click to expand)
Claude review from 7/16/2025, 1:55:35 AM (outdated - click to expand)
Claude review from 7/16/2025, 1:55:35 AM (outdated - click to expand)

Claude finished @steipete's task —— View job


📋 Summary

This PR addresses two distinct issues: an infinite loop in control message handling and UI simplification in the welcome screen. The control message fix prevents error responses from being re-processed indefinitely, while the UI change replaces a scrollable repository list with a cleaner count display.

✅ Positive Aspects

  • Root Cause Analysis: The infinite loop fix correctly identifies that response messages without pending handlers should not be processed further
  • UI/UX Improvement: The repository count display is more appropriate for limited screen space than a scrollable list
  • Clear Implementation: Both fixes are minimal and targeted, avoiding unnecessary complexity
  • Good Pluralization: Proper handling of singular/plural forms in the repository count text
  • Documentation: Added helpful mobile paste instructions to README

🔍 Areas for Improvement

Code Quality

  • The Swift code maintains consistent style and follows SwiftUI best practices
  • TypeScript changes are minimal and focused
  • Good use of early returns to simplify control flow

Potential Issues

  1. Async State Management: The repository scanning is asynchronous but doesn't handle potential race conditions if the user rapidly changes folders
  2. Empty State Messaging: "No repositories found" could be enhanced with helpful suggestions (e.g., "Try selecting a folder with Git projects")
  3. Large Directory Performance: No apparent limit on directory size when scanning - could hang on very large file systems

Performance

  • Repository scanning performs synchronous file I/O in a loop, which could block on network drives or slow filesystems
  • Consider adding a timeout or cancellation mechanism for the scan operation

Security

  • File path handling appears safe with proper tilde expansion
  • No obvious security concerns in the changes

Testing

  • No test coverage included for either fix
  • The control message loop fix would benefit from unit tests to prevent regression
  • UI changes could use snapshot tests

💡 Suggestions

1. Add Scan Cancellation

@State private var scanTask: Task<Void, Never>?

private func scanForRepositories() {
    scanTask?.cancel()  // Cancel any existing scan
    isScanning = true
    discoveredRepos = []
    
    scanTask = Task {
        // ... existing scanning logic
        
        // Check for cancellation periodically
        guard !Task.isCancelled else { return }
    }
}

2. Improve Error Feedback

@State private var scanError: String?

// In the UI:
if let error = scanError {
    Text(error)
        .font(.system(size: 11))
        .foregroundColor(.red)
}

3. Add Debug Logging for Message Loop Prevention

if (message.type === 'response') {
    logger.debug(`Ignoring response message that has no pending request: ${message.id}`, {
        category: message.category,
        action: message.action,
        error: message.error
    });
    return;
}

4. Consider Adding Tests

For the control message fix, a test could verify the loop prevention:

it('should not process response messages without pending requests', async () => {
    const handler = new ControlUnixHandler();
    const errorResponse = createControlResponse(mockMessage, null, 'Test error');
    
    // Spy on handler methods to ensure it doesn't recurse
    const spy = jest.spyOn(handler, 'handleMacMessage');
    
    await handler.handleMacMessage(errorResponse);
    
    expect(spy).toHaveBeenCalledTimes(1);
});

🎯 Priority Items

  1. Add timeout/cancellation for repository scanning to prevent UI freezes on slow filesystems
  2. Consider adding tests for the control message loop fix to prevent regression
  3. Monitor for edge cases where legitimate response messages might be incorrectly filtered

Overall, this is a solid PR that fixes real issues with minimal, focused changes. The fixes are appropriate and the code quality is good. The suggestions above would enhance robustness but aren't blockers for merging.


Reviewed commit: cd33867
Files changed: 3

Copy link

github-actions bot commented Jul 16, 2025

🔍 Code Quality Report

This comment is automatically updated with linting results from CI.

Node.js Biome Formatting ✅ Status: Passed

Node.js Biome Linting ✅ Status: Passed

Node.js Test Coverage ✅ Status: Passed

Client Coverage:
• Lines: 37.46%
• Functions: 50.45%
• Branches: 70.97%
• Statements: 37.46%

Server Coverage:
• Lines: 13.24%
• Functions: 35.43%
• Branches: 75.48%
• Statements: 13.24%

Mac Formatting (SwiftFormat) ❌ Status: Failed

Click to see details
Running SwiftFormat...
(lint mode - no files will be changed.)
Reading config file at /Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/.swiftformat
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Constants/RemoteAccessConstants.swift:5:1: error: (numberFormatting) Use consistent grouping for numeric literals. Groups will be separated by _ delimiters to improve readability. For each numeric type you can specify a group size (the number of digits in each group) and a threshold (the minimum number of digits in a number before grouping is applied).
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Constants/RemoteAccessConstants.swift:10:1: error: (linebreakAtEndOfFile) Add empty blank line at end of file.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:20:1: error: (wrap) Wrap lines that exceed the specified maximum width.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:21:1: error: (indent) Indent code in accordance with the scope level.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:21:1: error: (wrapMultilineStatementBraces) Wrap the opening brace of multiline statements.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:30:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:32:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:42:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:48:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:57:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Utilities/ViewExtensions.swift:62:1: error: (linebreakAtEndOfFile) Add empty blank line at end of file.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:14:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:18:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:23:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:28:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:30:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:34:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:36:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:41:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:49:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:56:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:60:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:65:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:69:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:72:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:81:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Core/Services/RemoteServicesStatusManager.swift:91:1: error: (linebreakAtEndOfFile) Add empty blank line at end of file.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Welcome/ProjectFolderPageView.swift:82:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Welcome/ProjectFolderPageView.swift:92:1: error: (wrap) Wrap lines that exceed the specified maximum width.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Welcome/ProjectFolderPageView.swift:93:1: error: (indent) Indent code in accordance with the scope level.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Welcome/ProjectFolderPageView.swift:93:1: error: (wrap) Wrap lines that exceed the specified maximum width.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Welcome/ProjectFolderPageView.swift:94:1: error: (indent) Indent code in accordance with the scope level.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Welcome/ProjectFolderPageView.swift:96:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Settings/DebugSettingsView.swift:142:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Settings/SecurityPermissionsSettingsView.swift:171:1: error: (wrap) Wrap lines that exceed the specified maximum width.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/VibeTunnelApp.swift:94:1: error: (wrap) Wrap lines that exceed the specified maximum width.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/VibeTunnelApp.swift:125:1: error: (modifierOrder) Use consistent ordering for member modifiers.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Settings/DashboardSettingsView.swift:83:1: error: (trailingSpace) Remove trailing space at end of a line.
/Users/administrator/actions-runner-2/_work/vibetunnel/vibetunnel/mac/VibeTunnel/Presentation/Views/Settings/DashboardSettingsView.swift:86:1: error: (trailingSpace) Remove trailing space at end of a line.
SwiftFormat completed in 0.15s.
Source input did not pass lint check.
8/117 files require formatting, 1 file skipped.

Mac Linting (SwiftLint) ✅ Status: Passed

macOS Test Coverage ❌ Status: Failed

• Coverage: 0%

steipete added 3 commits July 16, 2025 03:58
When all permissions are granted in the welcome screen, the checkmarks now align vertically on the left side for better visual consistency. Buttons remain centered when shown, but granted permissions align to leading edge.

- Added alignment: .leading to all three permission status frames
- Keeps checkmarks vertically aligned when all permissions are granted
- Maintains centered appearance for buttons when permissions not yet granted
Added a link to the blog post 'Command Your Claude Code Army Reloaded' at the bottom of the Control Agent Army welcome page for users who want to learn more about managing multiple AI agent sessions.

- Link styled as caption with accent color
- Opens in default browser
- Provides additional context for the feature
Removed 'Project Folder' and 'Discovered Repositories' labels to save space and simplify the UI. The interface elements are self-explanatory without these labels.

- Removed 'Project Folder' label above the path selector
- Removed 'Discovered Repositories' label above the repository count
- Adjusted spacing to maintain visual balance
- Creates a cleaner, less cluttered interface
Copy link

github-actions bot commented Jul 16, 2025

❌ CI Failed

View failed run

Failed Jobs:

  • Mac CI / Build, Lint, and Test macOS
    • Failed at: Run tests with coverage

steipete added 4 commits July 16, 2025 04:03
Added Spacers at top and bottom to vertically center the content on the welcome screen. This removes the excessive gap at the top and creates a more balanced layout.

- Added Spacer() before the title
- Added Spacer() after the tip text
- Content now appears centered on the page
Restructured the project folder page to match the consistent layout pattern used in the terminal selection page:
- Changed to VStack(spacing: 30) outer container
- Updated title to largeTitle font
- Updated description to body font with maxWidth: 480
- Changed to .padding() instead of .padding(.horizontal, 30)
- Set consistent width of 350 for interactive elements
- Properly nested VStacks for better vertical centering

This creates a more consistent and properly centered layout across all welcome pages.
The settings UI was using undefined dark-* classes (dark-border, dark-text, dark-text-muted) which caused fallback to white borders. Updated to use the proper theme-aware classes:
- border-dark-border → border-base
- bg-dark-bg-tertiary → bg-tertiary
- text-dark-text → text-primary
- text-dark-text-muted → text-muted

These classes properly use CSS variables that adapt to the current theme, providing the correct soft grey borders in dark mode.
Copy link

claude bot commented Jul 16, 2025

Claude review from 7/16/2025, 2:12:57 AM (outdated - click to expand)
Claude review from 7/16/2025, 2:12:57 AM (outdated - click to expand)

Claude finished @steipete's task —— View job


📋 Summary

This PR addresses two issues: fixing an infinite loop in control message handling for repository-path-update responses and simplifying the welcome screen to show repository count instead of a full list. The changes are minimal and focused on solving specific problems.

✅ Positive Aspects

  • Root Cause Fix: The infinite loop fix correctly identifies that response messages without pending handlers should not be reprocessed, preventing the error spiral
  • Clean Implementation: The fix is minimal (7 lines) and precisely targets the issue without over-engineering
  • Improved UX: Replacing the repository list with a count display is more appropriate for limited welcome screen space
  • Good Edge Case Handling: Proper singular/plural text handling ("1 repository" vs "X repositories")
  • Helpful Documentation: Added mobile paste instructions to README for better user guidance

🔍 Areas for Improvement

Code Quality

  • Swift Formatting Issues: CI shows multiple SwiftFormat violations (trailing spaces, line breaks) that need to be fixed
  • Missing Test Coverage: Mac tests show 0% coverage - the control message fix would benefit from unit tests
  • Synchronous I/O: Repository scanning performs synchronous file operations which could block on slow filesystems

Potential Issues

  1. Race Conditions: The repository scanning is async but doesn't handle rapid folder changes properly - multiple scans could overlap
  2. No Cancellation: No way to cancel an in-progress repository scan if user changes folders
  3. Performance on Large Directories: No limit on directory traversal - could hang on very large file systems
  4. Empty State UX: "No repositories found" could be more helpful (e.g., "Try selecting a folder with Git projects")

Performance

  • findGitRepositories uses synchronous FileManager operations in a loop
  • No timeout or size limits for directory scanning
  • Consider using async file operations or adding progress feedback

Security

  • File path handling appears safe with proper tilde expansion
  • Unix socket permissions correctly set to 0600
  • No obvious security vulnerabilities

Testing

  • The control message loop fix lacks test coverage
  • Consider adding integration tests for the message handler
  • UI changes could benefit from snapshot tests

💡 Suggestions

1. Add Scan Cancellation

@State private var scanTask: Task<Void, Never>?

private func scanForRepositories() {
    scanTask?.cancel()  // Cancel existing scan
    isScanning = true
    discoveredRepos = []
    
    scanTask = Task {
        let expandedPath = (selectedPath as NSString).expandingTildeInPath
        let repos = await findGitRepositories(in: expandedPath, maxDepth: 3)
        
        guard !Task.isCancelled else { return }
        
        await MainActor.run {
            discoveredRepos = repos.map { path in
                RepositoryInfo(name: URL(fileURLWithPath: path).lastPathComponent, path: path)
            }
            isScanning = false
        }
    }
}

2. Add Debug Logging

if (message.type === 'response') {
    logger.debug(`Ignoring response message that has no pending request: ${message.id}`, {
        category: message.category,
        action: message.action,
        error: message.error
    });
    return;
}

3. Add Test for Loop Prevention

describe('ControlUnixHandler', () => {
  it('should not process response messages without pending requests', async () => {
    const handler = new ControlUnixHandler();
    const errorResponse = createControlResponse(mockMessage, null, 'Test error');
    
    const spy = jest.spyOn(handler, 'handleMacMessage');
    await handler.handleMacMessage(errorResponse);
    
    expect(spy).toHaveBeenCalledTimes(1); // Should not recurse
  });
});

4. Fix Swift Formatting

Run ./scripts/lint.sh --fix in the mac directory to automatically fix the formatting issues flagged by CI.

🎯 Priority Items

  1. Fix SwiftFormat violations - CI is failing due to formatting issues
  2. Add cancellation for repository scanning - Prevents UI freezes on slow filesystems
  3. Add test coverage for the control message loop fix to prevent regression
  4. Consider adding timeout for repository scanning operations

Overall, this is a well-targeted PR that fixes real issues with minimal changes. The control message fix is particularly important as it prevents server instability. The UI simplification is a good UX improvement. With the formatting fixes and some additional safeguards for the scanning operation, this PR will be ready to merge.


Reviewed commit: af2b038
Files changed: 7

@github-actions github-actions bot deleted a comment from claude bot Jul 16, 2025
@github-actions github-actions bot deleted a comment from claude bot Jul 16, 2025
- Added detailed logging to track repository-path-update messages
- Fixed issue where Mac app updates weren't being processed correctly
- Added comprehensive tests for repository path synchronization
- Ensured updateRepositoryPath method properly invokes config callback
- Added tests for Mac message handling including edge cases
Copy link

claude bot commented Jul 16, 2025

Claude finished @steipete's task —— View job


📋 Summary

This PR addresses two distinct issues: an infinite loop in control message handling for repository-path-update responses and UI simplification in the welcome screen. The control message fix prevents error responses from being re-processed indefinitely, while the UI change replaces a scrollable repository list with a cleaner count display showing the number of discovered repositories.

✅ Positive Aspects

  • Root Cause Fix: The infinite loop fix correctly identifies that response messages without pending request handlers should not be reprocessed, preventing the error spiral that was causing server instability
  • Clean Implementation: The control message fix is minimal (7 lines) and precisely targets the issue without over-engineering
  • Improved UX: Replacing the full repository list with a simple count display is more appropriate for the limited welcome screen space
  • Good Edge Case Handling: Proper singular/plural text handling ("1 repository" vs "X repositories")
  • Test Coverage Added: New comprehensive test suite for control message handling covers happy path and error scenarios
  • Proper Logging: Added detailed logging for repository path updates to aid debugging

🔍 Areas for Improvement

Code Quality

  • Swift Formatting Issues: CI shows multiple SwiftFormat violations (trailing spaces, line breaks, wrapping) that need to be fixed
  • Synchronous I/O: Repository scanning in findGitRepositories performs synchronous file operations which could block on slow filesystems
  • Missing Test for Loop Prevention: While tests were added for repository path updates, there's no specific test verifying the infinite loop prevention logic

Potential Issues

  1. Race Conditions: The repository scanning is async but doesn't handle rapid folder changes properly - multiple scans could overlap if user rapidly changes folders
  2. No Cancellation: No way to cancel an in-progress repository scan if user changes folders, potentially leading to outdated results being displayed
  3. Performance on Large Directories: No limit on directory traversal depth beyond maxDepth=3 - could still hang on very large file systems
  4. Empty State UX: "No repositories found" could be more helpful (e.g., "Try selecting a folder with Git projects")

Performance

  • findGitRepositories uses synchronous FileManager operations in a loop without any throttling
  • No timeout or directory size limits for scanning operations
  • Could benefit from async file operations or progress feedback for large directories

Security

  • File path handling appears safe with proper tilde expansion (expandingTildeInPath)
  • Unix socket permissions correctly set to 0600 (owner read/write only)
  • No obvious security vulnerabilities in the changes

Testing

  • The control message loop fix itself lacks a specific test case
  • UI changes have no test coverage (snapshot tests would be beneficial)
  • Mac test coverage shows 0% in CI

💡 Suggestions

1. Add Scan Cancellation

@State private var scanTask: Task<Void, Never>?

private func scanForRepositories() {
    scanTask?.cancel()  // Cancel existing scan
    isScanning = true
    discoveredRepos = []
    
    scanTask = Task {
        let expandedPath = (selectedPath as NSString).expandingTildeInPath
        let repos = await findGitRepositories(in: expandedPath, maxDepth: 3)
        
        guard !Task.isCancelled else { return }
        
        await MainActor.run {
            discoveredRepos = repos.map { path in
                RepositoryInfo(name: URL(fileURLWithPath: path).lastPathComponent, path: path)
            }
            isScanning = false
        }
    }
}

2. Add Test for Loop Prevention

describe('Control message loop prevention', () => {
  it('should not process response messages without pending requests', async () => {
    const handler = controlUnixHandler as any;
    const errorResponse = {
      id: 'test-123',
      type: 'response' as const,
      category: 'system' as const,
      action: 'repository-path-update',
      error: 'Test error'
    };
    
    // Spy on handleMessage to ensure it doesn't recurse
    const spy = vi.spyOn(handler, 'handleMacMessage');
    
    // Clear any pending requests
    handler.pendingRequests.clear();
    
    // Process the response
    await handler.handleMacMessage(errorResponse);
    
    // Should only be called once, not recursively
    expect(spy).toHaveBeenCalledTimes(1);
    
    // Should not have tried to send another response
    expect(handler.sendToMac).not.toHaveBeenCalled();
  });
});

3. Add Progress Feedback

// In ProjectFolderPageView
@State private var scanProgress: Double = 0.0

// Show progress during scan
if isScanning {
    ProgressView(value: scanProgress, total: 1.0)
        .progressViewStyle(.linear)
        .frame(width: 350)
}

4. Fix Swift Formatting

Run ./scripts/lint.sh --fix in the mac directory to automatically fix all formatting issues.

5. Improve Empty State Message

} else if discoveredRepos.isEmpty {
    VStack(spacing: 4) {
        Text("No repositories found")
            .font(.system(size: 12))
            .foregroundColor(.secondary)
        Text("Try selecting a folder containing Git projects")
            .font(.system(size: 10))
            .foregroundColor(.tertiary)
    }
}

🎯 Priority Items

  1. Fix SwiftFormat violations - CI is failing due to formatting issues (run ./scripts/lint.sh --fix)
  2. Add scan cancellation - Prevents UI freezes and race conditions when changing folders rapidly
  3. Add specific test for the control message loop prevention to ensure regression protection
  4. Consider adding timeout for repository scanning operations on slow filesystems

Overall, this is a well-targeted PR that fixes real issues. The control message loop fix is particularly important as it prevents server instability. The UI simplification improves user experience by showing relevant information in a compact format. With the formatting fixes and additional safeguards for the scanning operation, this PR will be ready to merge.


Reviewed commit: d80ab69
Files changed: 8

@steipete steipete merged commit 2f3a421 into main Jul 16, 2025
15 checks passed
@steipete steipete deleted the loop-fix branch July 16, 2025 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant