Skip to content

fix: use sandboxed paths for Mac App Store build#592

Merged
PureWeen merged 5 commits intomainfrom
fix/maccatalyst-sandbox-paths
Apr 22, 2026
Merged

fix: use sandboxed paths for Mac App Store build#592
PureWeen merged 5 commits intomainfrom
fix/maccatalyst-sandbox-paths

Conversation

@jfversluis
Copy link
Copy Markdown
Collaborator

Implement platform-specific path resolution:

  • Create PlatformPaths.GetPolyPilotDir() that uses FileSystem.AppDataDirectory on MACCATALYST/iOS/Android (sandboxed), and home directory on desktop builds
  • Update CopilotService and ConnectionSettings to use PlatformPaths
  • Remove both /.polypilot/ and /.copilot/ temporary-exception entitlements from Entitlements.AppStore.plist

The sandbox automatically remaps HOME to ~/Library/Containers//Data/, so the bundled copilot CLI (with inherit: true in helper entitlements) will resolve UserProfile paths inside the container. This allows the app to work without requiring broad home-directory entitlements that Apple rejects.

@jfversluis jfversluis force-pushed the fix/maccatalyst-sandbox-paths branch 2 times, most recently from 3667dff to 48ea2c0 Compare April 16, 2026 14:08
@PureWeen
Copy link
Copy Markdown
Owner

PureWeen commented Apr 16, 2026

🔍 Multi-Model Code Review — PR #592 (Re-review after fix)

fix: use sandboxed paths for Mac App Store build by @jfversluis
Fix commit: 677f14131 — trust UserProfile sandbox remapping instead of FileSystem.AppDataDirectory

CI Status: ⚠️ No CI checks reported on this branch
Reviewer Consensus: 3/3 reviewers agree — all critical issues resolved


Previous Finding Status

Previous Finding Status
🔴 Migration/read path mismatch — UserProfile/.polypilot/ vs AppDataDirectory/.polypilot/ (data loss) FIXEDGetPolyPilotDirOverride() no longer redirects to FileSystem.AppDataDirectory; returns null in production. All callsites use UserProfile, matching the migration destination.
🔴 FileSystem.AppDataDirectory is the wrong API — sandbox remaps UserProfile already FIXED — The #if MACCATALYST + FileSystem.AppDataDirectory block was removed entirely. Behavior now correctly relies on sandbox HOME remapping.
🟡 Silent exception fallback in GetPolyPilotDirOverride() catch block FIXED — The try/catch block no longer exists. Method is now trivial: test override or null.
🟢 Static caches in PluginLoader/ShowImageTool bypass SetForTesting() ⚠️ STILL PRESENT_pluginsDir and _imagesDir use ??= caching. Once populated, SetForTesting() has no effect on cached values. Low-severity: in production GetPolyPilotDirOverride() returns null, so caches hold the correct UserProfile-based path. Only matters if production logic is later added to GetPolyPilotDirOverride().
🟢 GetCopilotDirOverride() is effectively dead code STILL PRESENT, ACCEPTABLE — No production callers. Plausible forward-looking infrastructure for .copilot/ paths. Harmless.
🟢 GetImagesDir_() trailing underscore naming STILL PRESENT — Non-functional naming nit.

Current State Assessment

The fix commit correctly resolves both critical data-loss issues by removing the #if MACCATALYST block that used FileSystem.AppDataDirectory. The design insight — Mac Catalyst's sandbox remaps HOME so UserProfile-based paths resolve inside the container automatically — eliminates the entire class of path-mismatch bugs and aligns with the existing .copilot/ approach.

What the PR now delivers:

  • PlatformPaths class providing centralized test isolation (SetForTesting()) for all 12+ path sites
  • PlatformPaths.cs added to test csproj's <Compile Include> list
  • All callsites consistently check GetPolyPilotDirOverride() first, providing a single point of control if future platform-specific overrides are needed
  • Clear documentation in PlatformPaths explaining why no production override is needed

Remaining low-severity items (non-blocking):

Severity Finding Flagged by
🟢 MINOR PluginLoader/ShowImageTool static caches not resettable via PlatformPaths.SetForTesting() 2/3 reviewers
🟢 MINOR GetCopilotDirOverride() has no production callers 2/3 reviewers
🟢 MINOR GetImagesDir_() trailing underscore — consider ComputeImagesDir() 2/3 reviewers
🟢 MINOR PlatformPaths.SetForTesting() not wired into TestSetup.cs — unused infrastructure until needed 1/3 reviewers

Test Results

  • 3510 passed, 0 failed, 0 skipped
  • PlatformPaths.cs compile-included in test project
  • No new test failures introduced

Recommendation: ✅ Approve

All critical and moderate issues from the previous review are resolved. The remaining items are low-severity, non-blocking improvements that can be addressed in follow-up PRs. The approach of trusting sandbox HOME remapping is correct, simple, and eliminates path-mismatch bugs.

jfversluis and others added 2 commits April 16, 2026 22:09
- Create PlatformPaths.GetPolyPilotDirForMacCatalyst() that returns
  FileSystem.AppDataDirectory/.polypilot/ for sandboxed Mac Catalyst,
  null for all other platforms
- Update CopilotService and ConnectionSettings to check for sandbox path
  first, then fall back to platform-specific defaults
- Remove both /.polypilot/ and /.copilot/ temporary-exception
  entitlements from Entitlements.AppStore.plist

This change is Mac App Store-specific only. iOS, Android, and
Developer ID builds use their existing path logic unchanged.

The sandbox automatically remaps HOME to ~/Library/Containers/<bundle-id>/Data/,
so UserProfile-based paths in the bundled copilot CLI will resolve
inside the container.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address review feedback on PR #592: the initial commit only updated
2 of 12+ path sites, creating a split-brain where some services
used the sandbox container while others used the home directory.

Now ALL services route through PlatformPaths.GetPolyPilotDirOverride():
- CopilotService, ConnectionSettings (already done)
- ServerManager, PromptLibraryService, ScheduledTaskService
- ChatDatabase, AuditLogService, RepoManager
- PluginLoader, ShowImageTool, PluginFileLogger
- MauiProgram (crash log), App.xaml.cs (pending navigation)
- MacCatalyst/Program (instance lock, navigation sidecar)
- MacCatalyst/NotificationManagerService (pending navigation)

Added PlatformPaths.SetForTesting() for test isolation.
Added PlatformPaths.GetCopilotDirOverride() (returns  sandboxnull
remaps HOME so ~/.copilot/ resolves inside the container already).

No migration needed: this is the first Mac App Store release.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jfversluis jfversluis force-pushed the fix/maccatalyst-sandbox-paths branch from 0f7729f to 85ba5ca Compare April 16, 2026 20:09
PureWeen and others added 3 commits April 21, 2026 09:17
…aDirectory

On Mac Catalyst, the sandbox remaps HOME (UserProfile) into
~/Library/Containers/<bundle-id>/Data/, so ~/.polypilot/ already
resolves inside the container — same as ~/.copilot/.

Using FileSystem.AppDataDirectory added an extra /Library/ segment,
creating a path mismatch with the migration in MigrateLegacyDataIfNeeded()
and causing silent data loss for upgrading users.

Remove the MACCATALYST-specific FileSystem.AppDataDirectory override.
GetPolyPilotDirOverride() now returns null on all platforms (test
overrides still work via SetForTesting). All 12+ callsite overrides
remain and correctly fall through to existing UserProfile-based logic.

Also add missing PlatformPaths.cs Compile Include in test csproj.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove dead GetCopilotDirOverride() and _testCopilotDir (no callers)
- Rename GetImagesDir_() to ComputeImagesDir() for clarity
- Rename GetPluginsDir() to ComputePluginsDir() for consistency
- Add ResetCachedPathForTesting() to PluginLoader and ShowImageTool
  so PlatformPaths.SetForTesting() invalidates their static caches
- Wire PlatformPaths.SetForTesting() into TestSetup.cs ModuleInitializer
- Update TestSetup isolation doc comment

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PureWeen PureWeen merged commit 9190213 into main Apr 22, 2026
@PureWeen PureWeen deleted the fix/maccatalyst-sandbox-paths branch April 22, 2026 17:43
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