Skip to content

Fix hang-on-launch issue on macOS Sequoia (#169)#170

Merged
schuyler merged 4 commits intomainfrom
claude/resolve-issue-169-018cib4nr39PhoSqrfiji5PG
Nov 23, 2025
Merged

Fix hang-on-launch issue on macOS Sequoia (#169)#170
schuyler merged 4 commits intomainfrom
claude/resolve-issue-169-018cib4nr39PhoSqrfiji5PG

Conversation

@schuyler
Copy link
Copy Markdown
Owner

Summary

Fixes a critical bug where MacDown 3000 hangs immediately on launch on macOS Sequoia 15.7.2. The root cause was the preferences migration code performing synchronous operations on the main thread during app initialization, which times out on Sequoia due to stricter sandbox restrictions.

Changes Made

1. Preferences Migration (MPPreferences.m)

  • Added 2-second timeout protection using dispatch_semaphore for both phases of migration
  • Background queue execution to prevent blocking main thread
  • Comprehensive logging with [MPPreferences] prefix for diagnostics
  • Error handling with @try/@catch/@finally blocks
  • Graceful fallback to default preferences if migration fails or times out
  • Removed deprecated synchronize calls (deprecated since macOS 10.13)
  • Replaced custom initWithSuiteNamed: with Apple's built-in initWithSuiteName:

2. CI/CD Enhancements (.github/workflows/test.yml)

  • Added macOS 15 (Sequoia) to test matrix alongside macOS 14
  • fail-fast: false ensures both OS versions are tested
  • Comprehensive runtime launch test (smoke test for issue Crash on start #169):
    • Test 1: Fresh install scenario (no legacy preferences)
    • Test 2: Migration scenario (legacy preferences present)
    • Both scenarios verified on macOS 14 and 15
    • Timeout detection ensures app never hangs
    • Validates migration completion flag
  • Fixed artifact naming to prevent conflicts between matrix jobs

3. Documentation Updates

  • Updated infrastructure_evaluation.md to reflect macOS 15 testing
  • Updated test_coverage_improvement_plan.md to document timeout protection

Testing Approach

Automated Testing

Unit tests pass on macOS 14 and 15
Fresh install test - App launches without hanging
Migration test - App launches with legacy preferences without hanging
Timeout detection - Would fail if app hangs
Both OS versions tested (Sonoma and Sequoia)

Manual Testing Plan

Zeppo assessed that manual testing has limited value given the comprehensive automated coverage, but provided a minimal testing plan for real-world validation on macOS Sequoia hardware:

  1. Fresh Install Test: Verify app launches within 2-3 seconds with no preferences
  2. Migration Test: Create legacy preferences and verify smooth migration
  3. Console Logs Test: Verify helpful diagnostic logging
  4. No Retry Loop Test: Ensure migration doesn't retry on subsequent launches

See Zeppo's analysis for detailed steps.

Code Review

Chico reviewed the implementation and identified two critical issues, which have been addressed:

  1. Phase 2 timeout protection - Added timeout to preference copying phase
  2. Removed deprecated synchronize - Eliminated blocking disk writes on main thread

Technical Details

Migration Flow:

  1. Check if already migrated (early return)
  2. Phase 1: Read legacy preferences from com.uranusjr.macdown (background queue, 2s timeout)
  3. Phase 2: Copy preferences to app.macdown.macdown3000 (background queue, 2s timeout)
  4. Mark migration complete (prevents retry loops)
  5. Graceful fallback to defaults on any failure

Timeout Protection:

  • Both phases run on background queue
  • 2-second timeout per phase (generous but not excessive)
  • App continues launch even if migration fails
  • Migration marked complete to prevent repeated hangs

Error Handling:

  • @try/@catch blocks around all risky operations
  • @finally ensures semaphores always signal (no deadlocks)
  • Comprehensive logging for debugging
  • All failures mark migration as complete

Related Issue

Related to #169

Review Notes

Architectural Guidance: Groucho validated the approach and confirmed proper Objective-C patterns
Code Review: Chico identified and all critical issues were addressed
Documentation: Harpo updated relevant documentation
Testing: Zeppo provided manual testing assessment

CI Status: ✅ All tests passing on macOS 14 and 15

This commit addresses a critical bug where MacDown 3000 hangs immediately
on launch on macOS Sequoia 15.7.2. The root cause was the preferences
migration code performing synchronous operations on the main thread during
app initialization, which times out on Sequoia due to stricter sandbox
restrictions.

Changes made:

1. Preferences Migration (MPPreferences.m):
   - Add 2-second timeout protection around migration using dispatch_semaphore
   - Run migration on background queue to prevent blocking main thread
   - Add comprehensive logging for diagnostics
   - Add error handling with @try/@catch blocks
   - Mark migration as complete on timeout/failure to prevent retry loops
   - Replace custom initWithSuiteNamed: with Apple's built-in initWithSuiteName:
   - Gracefully fall back to default preferences if migration fails

2. CI Enhancements (test.yml):
   - Add macOS 15 (Sequoia) to test matrix alongside macOS 14
   - Add fail-fast: false to test both OS versions
   - Add comprehensive runtime launch test (smoke test)
   - Test both fresh install and migration scenarios
   - Verify app launches within timeout (detects hangs)
   - Validate migration completion flag is set correctly

Testing approach:
- Fresh install scenario: No legacy preferences
- Migration scenario: Legacy preferences present
- Both scenarios verified on macOS 14 and 15
- Timeout protection ensures app never hangs

Related to #169
Each OS matrix job was trying to upload artifacts with the same name,
causing conflicts. Now artifacts are named uniquely per OS version.
Following Chico's code review, this commit fixes two critical issues:

1. Added timeout protection to Phase 2 (preference copying)
   - Wrapped preference copying in background queue with semaphore
   - Added 2-second timeout for the copy operation
   - Prevents hang if writing preferences blocks on disk I/O

2. Removed all deprecated synchronize calls
   - Removed 5 synchronize() calls (deprecated since macOS 10.13)
   - NSUserDefaults auto-syncs periodically
   - Eliminates potential lag from synchronous disk writes on main thread

Both phases of migration now have timeout protection and run on
background queues, ensuring the app never hangs during startup.

Related to #169
Updated by Harpo to reflect changes from issue #169:
- CI now tests on both macOS 14 and macOS 15 (Sequoia)
- Added runtime launch tests for hang detection
- Preferences migration now has timeout protection

Related to #169
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Nov 23, 2025

Code Coverage Report

Current Coverage: 41.18%

Coverage Details (Summary)
Name                                                                                                                                   Coverage            
-------------------------------------------------------------------------------------------------------------------------------------- ------------------- 
MASPreferences.bundle                                                                                                                  0.00% (0/0)         
MacDown 3000.app                                                                                                                       54.19% (6735/12429) 
    /Users/runner/work/macdown3000/macdown3000/MacDown/Code/Document/MPDocument.m                                                      49.83% (733/1471)   
        MPEditorPreferenceKeyWithValueKey                                                                                              85.71% (6/7)        
        MPEditorKeysToObserve                                                                                                          100.00% (14/14)     
        __MPEditorKeysToObserve_block_invoke                                                                                           100.00% (9/9)       
        MPEditorPreferencesToObserve                                                                                                   100.00% (13/13)     
        __MPEditorPreferencesToObserve_block_invoke                                                                                    100.00% (8/8)       
        MPRectStringForAutosaveName                                                                                                    100.00% (6/6)       
        MPGetWebViewBackgroundColor                                                                                                    0.00% (0/9)         
        -[NSURL(Convert) absoluteBaseURLString]                                                                                        0.00% (0/6)         
        -[WebView(Shortcut) enclosingScrollView]                                                                                       100.00% (3/3)       
        -[MPPreferences(Hoedown) extensionFlags]                                                                                       71.43% (20/28)      
        -[MPPreferences(Hoedown) rendererFlags]                                                                                        66.67% (8/12)       
        MPGetPreviewLoadingCompletionHandler                                                                                           100.00% (25/25)     
        __MPGetPreviewLoadingCompletionHandler_block_invoke                                                                            100.00% (22/22)     
        -[MPDocument preferences]                                                                                                      100.00% (3/3)       
        -[MPDocument markdown]                                                                                                         100.00% (3/3)       
        -[MPDocument setMarkdown:]                                                                                                     100.00% (3/3)       
        -[MPDocument html]                                                                                                             0.00% (0/3)         
        -[MPDocument toolbarVisible]                                                                                                   0.00% (0/3)         
        -[MPDocument previewVisible]                                                                                                   100.00% (3/3)       
        -[MPDocument editorVisible]                                                                                                    100.00% (3/3)       
        -[MPDocument needsHtml]                                                                                                        0.00% (0/5)         
        -[MPDocument setTotalWords:]                                                                                                   0.00% (0/7)         
        -[MPDocument setTotalCharacters:]                                                                                              0.00% (0/7)         
        -[MPDocument setTotalCharactersNoSpaces:]                                                                                      0.00% (0/8)         
        -[MPDocument setAutosaveName:]                                                                                                 100.00% (4/4)       
        -[MPDocument init]                                                                                                             88.89% (8/9)        
        -[MPDocument windowNibName]                                                                                                    100.00% (3/3)       
        -[MPDocument windowControllerDidLoadNib:]                                                                                      98.81% (83/84)      
        __41-[MPDocument windowControllerDidLoadNib:]_block_invoke                                                                     100.00% (4/4)       
        -[MPDocument reloadFromLoadedString]                                                                                           100.00% (8/8)       
        -[MPDocument close]                                                                                                            0.00% (0/19)        
        +[MPDocument autosavesInPlace]                                                                                                 100.00% (2/2)       
        +[MPDocument writableTypes]                                                                                                    100.00% (3/3)       
        -[MPDocument isDocumentEdited]                                                                                                 100.00% (5/5)       
        -[MPDocument writeToURL:ofType:error:]                                                                                         0.00% (0/15)        
        -[MPDocument dataOfType:error:]                                                                                                0.00% (0/3)         
        -[MPDocument readFromData:ofType:error:]                                                                                       100.00% (8/8)       
        -[MPDocument prepareSavePanel:]                                                                                                76.92% (30/39)      
        __31-[MPDocument prepareSavePanel:]_block_invoke                                                                               100.00% (12/12)     
        -[MPDocument printInfo]                                                                                                        0.00% (0/12)        
        -[MPDocument printOperationWithSettings:error:]                                                                                0.00% (0/7)         
        -[MPDocument printDocumentWithSettings:showPrintPanel:delegate:didPrintSelector:contextInfo:]                                  0.00% (0/17)        
        -[MPDocument validateUserInterfaceItem:]                                                                                       0.00% (0/32)        
        -[MPDocument splitViewDidResizeSubviews:]                                                                                      100.00% (4/4)       
        -[MPDocument textView:doCommandBySelector:]                                                                                    0.00% (0/13)        

... (2056 more lines truncated)

📊 **Full coverage report available in workflow artifacts**

@schuyler schuyler merged commit 8026729 into main Nov 23, 2025
5 checks passed
@schuyler schuyler mentioned this pull request Nov 24, 2025
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.

2 participants