Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ jobs:
/usr/libexec/PlistBuddy -c "Print :NSExtension:NSExtensionAttributes:NSExtensionServiceRoleType" "$extension_info" | grep -Fx NSExtensionServiceRoleTypeEditor
/usr/libexec/PlistBuddy -c "Print :NSExtension:NSExtensionAttributes:NSExtensionServiceAllowsFinderPreviewItem" "$extension_info" | grep -Fx true
/usr/libexec/PlistBuddy -c "Print :NSExtension:NSExtensionAttributes:NSExtensionActivationRule" "$extension_info" | grep -Fx 'SUBQUERY (extensionItems, $extensionItem, SUBQUERY ($extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.item").@count == $extensionItem.attachments.@count).@count == 1'
- name: Preflight sandboxed Mac App Store archive
env:
SORTING_HAT_APP_STORE_DERIVED_DATA: ${{ runner.temp }}/SortingHatAppStoreDerivedData
SORTING_HAT_APP_STORE_ARCHIVE: ${{ runner.temp }}/SortingHat-AppStore-Preflight.xcarchive
run: ./script/preflight_app_store.sh
18 changes: 18 additions & 0 deletions Configuration/AppStoreExportOptions.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>distributionBundleIdentifier</key>
<string>com.tcballard.sortinghat</string>
<key>manageAppVersionAndBuildNumber</key>
<false/>
<key>method</key>
<string>app-store-connect</string>
<key>signingStyle</key>
<string>automatic</string>
<key>teamID</key>
<string>R8HXTBY3NM</string>
</dict>
</plist>
32 changes: 32 additions & 0 deletions Configuration/PrivacyInfo.xcprivacy
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
<string>3B52.1</string>
</array>
</dict>
</array>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Configuration/SendToSortingHatAction-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>2</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
Expand Down
18 changes: 18 additions & 0 deletions Configuration/SortingHatApp-AppStore.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>R8HXTBY3NM.com.tcballard.sortinghat</string>
</array>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
6 changes: 5 additions & 1 deletion Configuration/SortingHatApp-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>2</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key>
Expand All @@ -32,5 +34,7 @@
<string>NSApplication</string>
<key>SortingHatAppGroupIdentifier</key>
<string>R8HXTBY3NM.com.tcballard.sortinghat</string>
<key>SortingHatLocalOnlyDistribution</key>
<string>$(SORTING_HAT_LOCAL_ONLY)</string>
</dict>
</plist>
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ let package = Package(
platforms: [.macOS(.v14)],
products: [
.library(name: "SortingHatCore", targets: ["SortingHatCore"]),
.library(name: "SortingHatFMResearch", targets: ["SortingHatFMResearch"]),
.library(name: "SortingHatFinderAdapter", targets: ["SortingHatFinderAdapter"]),
.executable(name: "sorting-hat", targets: ["SortingHat"]),
.executable(name: "SortingHatApp", targets: ["SortingHatApp"]),
],
targets: [
.target(name: "SortingHatQueueLock"),
.target(name: "SortingHatCore", dependencies: ["SortingHatQueueLock"]),
.target(name: "SortingHatFMResearch", dependencies: ["SortingHatCore"]),
.target(name: "SortingHatFinderAdapter", dependencies: ["SortingHatCore"]),
.executableTarget(name: "SortingHat", dependencies: ["SortingHatCore"]),
.executableTarget(name: "SortingHat", dependencies: ["SortingHatCore", "SortingHatFMResearch"]),
.executableTarget(name: "SortingHatApp", dependencies: ["SortingHatCore"]),
.testTarget(
name: "SortingHatTests",
dependencies: ["SortingHatCore", "SortingHatFinderAdapter"],
dependencies: ["SortingHatCore", "SortingHatFMResearch", "SortingHatFinderAdapter"],
resources: [.process("Fixtures")]
),
]
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
[See exactly how the product extends Apple’s WWDC26 file-sorting demo, including the passing shipping-path benchmark and its limitations.](docs/wwdc26-comparison.md)

> [!WARNING]
> The current `v0.1.0` build is an experimental pre-release. Its app bundle is ad-hoc signed, not signed with an Apple Developer ID, and it is not notarized. Gatekeeper may block the first launch; managed Macs may prohibit it entirely. Homebrew installation confirms the archive and cask are valid, but does not bypass these macOS security checks.
> The downloadable GitHub `v0.1.0` artifact is an experimental pre-release. Its app bundle is ad-hoc signed, not signed with an Apple Developer ID, and it is not notarized. Gatekeeper may block the first launch; managed Macs may prohibit it entirely. Homebrew installation confirms the archive and cask are valid, but does not bypass these macOS security checks. This warning does not describe the separately signed Mac App Store build.

## Requirements

Expand All @@ -30,7 +30,7 @@ mkdir -p ~/SortingHat/Inbox

## Menu-bar app

The packaged app is currently intended for testing, not polished public distribution. If Gatekeeper blocks it, macOS may offer **System Settings → Privacy & Security → Open Anyway**. Only override Gatekeeper if you understand and accept the risk; the Finder Quick Action may produce additional trust prompts.
The local and GitHub-packaged app is currently intended for testing. If Gatekeeper blocks the GitHub build, macOS may offer **System Settings → Privacy & Security → Open Anyway**. Only override Gatekeeper if you understand and accept the risk; the Finder Quick Action may produce additional trust prompts. The separately signed Mac App Store build follows Apple's Store trust and sandbox path.

Build and open the native companion with:

Expand Down Expand Up @@ -89,7 +89,7 @@ rules:
- Put everything else in Files/YYYY-MM and add one useful topic tag.
```

Choose **Automatic**, **Apple**, **Ollama**, or **OpenAI** under **Model Settings**. Apple can use the local `system` model, Private Cloud Compute (`pcc`), or an automatic policy. Automatic always tries on-device first and retries PCC only after an availability or generation failure. Content extraction, unsafe paths, and invalid filing decisions never trigger cloud escalation. PCC is disabled until `allow_apple_pcc: true` is explicitly saved; enabling it means file context may be sent to Apple's Private Cloud Compute service. Overall provider selection then falls back to configured Ollama and OpenAI providers when Apple is unavailable.
Choose **Automatic**, **Apple**, **Ollama**, or **OpenAI** under **Model Settings** in source and Developer ID builds. Apple's shipping app integration uses the on-device Foundation Model; automatic provider selection can fall back to configured providers when Apple is unavailable. The Mac App Store build is deliberately local-only: it offers Apple on-device processing and Ollama restricted to loopback addresses on the same Mac, with no OpenAI or remote Ollama route. Content extraction, unsafe paths, and invalid filing decisions never trigger cloud escalation. Private Cloud Compute remains research-only and is not exposed by the shipping app.

For the on-device system model, `apple_use_case: content-tagging` opts into the Foundation Models framework's content-tagging specialization. `apple_guardrails: permissive-content-transformations` relaxes the system model's content-transformation guardrails for filing material that the default policy refuses; use the default unless your rules require that behavior. These system-only options are omitted from PCC requests. Apple requests use in-process guided generation and greedy sampling. The app stores the OpenAI API key in macOS Keychain; the CLI reads `OPENAI_API_KEY`.

Expand Down Expand Up @@ -140,6 +140,9 @@ Bounded tool-calling candidates are also evaluated there under a documented thre
```sh
swift test
./script/generate_xcode_project.sh
./script/preflight_app_store.sh
# After one-time notarytool Keychain setup:
./script/release_local.sh 0.2.0
```

Inference is behind `FileAnalyzing`, so filesystem behavior and safety can be tested without a model. Provider selection prefers Apple's in-process Foundation Models framework when available and otherwise uses the configured local Ollama endpoint.
Expand All @@ -149,3 +152,5 @@ The Foundation Models decision path can be shared by a future iPhone or iPad cli
## Release status

Release archives are currently ad-hoc signed and published as GitHub pre-releases. Before treating Sorting Hat as production-ready, releases must be signed with a **Developer ID Application** certificate using the hardened runtime, submitted to Apple for notarization, stapled, and validated with Gatekeeper. See [Apple's Gatekeeper guidance](https://support.apple.com/en-gb/102445) and [Apple's notarization documentation](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution).

Developer ID/Homebrew and Mac App Store are separate tracked channels. App Store Connect build **0.1.0 (1)** has been uploaded and is selectable; product-page metadata, privacy declarations, review submission, and installed-build verification remain. The GitHub pre-release is still an ad-hoc-signed experimental artifact and is not equivalent to the Store build. See the [distribution guide](docs/distribution.md), [Issue #24](https://github.com/tcballard/SortingHat/issues/24), and [Issue #29](https://github.com/tcballard/SortingHat/issues/29).
Loading
Loading