From d03489073e589d874d71b8d16913f101d55011df Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Wed, 22 Jul 2026 06:19:39 +0100 Subject: [PATCH 1/4] docs(readme): sharpen local-first front door [issue:#29] Lead with the plain-language filing workflow, the Store build's local-only provider boundary, and a genuine product screenshot. Remove the obsolete above-the-fold warning while retaining detailed distribution caveats in their channel-specific sections. --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 924139f..aa53380 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # Sorting Hat -**A drop folder with opinions.** Sorting Hat is a native macOS filing assistant for people who want a clean Inbox without writing automation: it uses plain-language rules and Apple’s on-device Foundation Model to inspect, rename, Finder-tag, and route files, while deterministic Swift code validates every filesystem action. +**A drop folder with opinions.** + +Sorting Hat is a local-first macOS filing assistant for anyone who wants a clean Inbox without writing automation. Describe how receipts, screenshots, documents, and downloads should be named and filed; Sorting Hat turns that plan into editable rules, extracts useful context, and validates every rename, Finder tag, and destination in Swift before touching the filesystem. The Mac App Store build uses only Apple’s on-device Foundation Model or Ollama running on the same Mac. + +![Sorting Hat showing renamed files and their rule-specific destinations](launch-pack/outputs/app-store/screenshots/01-sorted-activity.jpg) + +Build and run from source: ```sh ./script/build_and_run.sh @@ -8,9 +14,6 @@ [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 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 - macOS 14 or later for the menu-bar app From 4a0f3aa31bebcc3691448aaaa056fc83a33cb1f0 Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Wed, 22 Jul 2026 06:30:28 +0100 Subject: [PATCH 2/4] docs(release): refresh App Store launch evidence [issue:#29] Reconciles the launch pack with merged PR #30 and selected local-only build 0.1.0 (2). Records verified public links, uploaded assets, saved review metadata, the README channel, and the remaining owner-controlled submission gates. --- docs/distribution.md | 16 +++++++-------- launch-pack/ASSET_INVENTORY.md | 8 ++++---- launch-pack/CHANNEL_MATRIX.md | 9 +++++---- launch-pack/CLAIMS_LEDGER.md | 7 ++++--- launch-pack/HANDOFF.md | 15 +++++++------- launch-pack/LAUNCH_BRIEF.md | 20 +++++++++---------- launch-pack/RELEASE_CHECKLIST.md | 4 ++-- launch-pack/outputs/app-store/REVIEW_NOTES.md | 2 +- .../outputs/app-store/SUBMISSION_CHECKLIST.md | 2 +- 9 files changed, 43 insertions(+), 40 deletions(-) diff --git a/docs/distribution.md b/docs/distribution.md index 701e794..3f5e041 100644 --- a/docs/distribution.md +++ b/docs/distribution.md @@ -86,14 +86,14 @@ App Store Connect record for `com.tcballard.sortinghat`. Then archive the Connect**, or export a correctly signed archive with `Configuration/AppStoreExportOptions.plist`. -App Store Connect build **0.1.0 (1)** is uploaded and selectable. Before -submission, complete the product page, support and privacy URLs, screenshots, -age rating, App Privacy answers, export-compliance answers, and review notes. -The submission pack under [`launch-pack/`](../launch-pack/) contains prepared -copy, genuine screenshots, icon evidence, review instructions, and an explicit -blocker checklist. Review notes explain the dashboard and menu-bar entry points, -the required user-selected Inbox and output folders, how to enable the bundled -Finder Action, and how to exercise sorting without credentials. +App Store Connect build **0.1.0 (2)** passed Apple validation, processed as +`VALID`, and is selected for version 0.1.0. The listing, screenshots, +categories, age rating, review contact, and review notes are saved. Before +submission, choose pricing, attest **Data Not Collected**, complete export +compliance and content rights, and run the installed-build smoke test. The +submission pack under [`launch-pack/`](../launch-pack/) contains the verified +copy, genuine screenshots, icon evidence, review instructions, and remaining +blocker checklist. An App Store/TestFlight build must still be manually verified for first-run setup, persisted folder access across relaunch, on-device sorting, manual review, diff --git a/launch-pack/ASSET_INVENTORY.md b/launch-pack/ASSET_INVENTORY.md index d0041b1..917c7c7 100644 --- a/launch-pack/ASSET_INVENTORY.md +++ b/launch-pack/ASSET_INVENTORY.md @@ -2,7 +2,7 @@ | ID | Asset | Purpose | Source or provenance | Constraints | Output path | Status | Blocker or next action | | --- | --- | --- | --- | --- | --- | --- | --- | -| A01 | 1024 px app icon | Embedded Store icon reference and visual handoff | Shipping AppIcon asset, SHA-256 recorded in evidence | Opaque 1024×1024 PNG | outputs/app-store/icon/app-icon-1024.png | Ready | Confirm the selected build displays this icon in App Store Connect | -| A02 | Sorted Activity screenshot | Show the core result | Genuine app capture using synthetic fixtures | 1440×900 opaque JPEG | outputs/app-store/screenshots/01-sorted-activity.jpg | Ready | Upload in position 1 | -| A03 | Plain-language Rules screenshot | Show rule creation and editing | Genuine app capture with no private data | 1440×900 opaque JPEG | outputs/app-store/screenshots/02-plain-language-rules.jpg | Ready | Upload in position 2 | -| A04 | Privacy settings screenshot | Avoid a stale PCC claim | Earlier capture showed a non-shipping setting | Must reflect current product | Not included | Removed | Capture a replacement only after the privacy/provider decision | +| A01 | 1024 px app icon | Embedded Store icon reference and visual handoff | Shipping AppIcon asset, SHA-256 recorded in evidence | Opaque 1024×1024 PNG | outputs/app-store/icon/app-icon-1024.png | Ready | Embedded in selected build 2 | +| A02 | Sorted Activity screenshot | Show the core result | Genuine app capture using synthetic fixtures | 1440×900 opaque JPEG | outputs/app-store/screenshots/01-sorted-activity.jpg | Ready | Uploaded in position 1 | +| A03 | Plain-language Rules screenshot | Show rule creation and editing | Genuine app capture with no private data | 1440×900 opaque JPEG | outputs/app-store/screenshots/02-plain-language-rules.jpg | Ready | Uploaded in position 2 | +| A04 | Privacy settings screenshot | Avoid a stale PCC claim | Earlier capture showed a non-shipping setting | Must reflect current product | Not included | Removed | Not required for this submission | diff --git a/launch-pack/CHANNEL_MATRIX.md b/launch-pack/CHANNEL_MATRIX.md index 6761b78..be17cec 100644 --- a/launch-pack/CHANNEL_MATRIX.md +++ b/launch-pack/CHANNEL_MATRIX.md @@ -2,7 +2,8 @@ | Channel | Audience | Desired action | Deliverable | Claim IDs | Assets and links | Owner or specialist | Output path | Authority | Status | Blocker or next action | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| Mac App Store listing | Mac users | Understand and download | Name, subtitle, promotional text, description, keywords, categories | C01, C02, C03 | A01-A03 | Tom Ballard | outputs/app-store/LISTING.md | Owner enters and submits | Ready | Resolve version and price before submission | -| App Review | Apple reviewer | Exercise the app without credentials | Review notes and contact checklist | C01-C03 | Support and privacy URLs | Tom Ballard | outputs/app-store/REVIEW_NOTES.md | Owner enters and submits | Ready | Owner supplies phone number | -| App Privacy | Apple reviewer and users | Understand data handling | Conservative questionnaire guidance and public policy | C02, C04, C05 | docs/privacy.md | Tom Ballard | outputs/app-store/APP_PRIVACY.md | Owner attests in App Store Connect | Blocked | Decide whether OpenAI remains in the Store build | -| Support | Existing users | Resolve setup and review issues | Public support page | C01-C03 | docs/support.md | Tom Ballard | docs/support.md | Public after merge | Ready | Merge PR #30 before using the URL | +| Repository front door | Evaluators and source users | Understand the product and inspect or build it | README identity, local-first contract, screenshot, source command, and proof link | C01, C02, C03, C07 | A02 and WWDC26 comparison | Merge & Tell + README Front Door | README.md | Owner merges PR #31 | Ready | Review and merge PR #31 | +| Mac App Store listing | Mac users | Understand and download after approval | Name, subtitle, promotional text, description, keywords, categories | C01, C02, C03, C07 | A01-A03 | Tom Ballard | outputs/app-store/LISTING.md | Owner submits and releases | Ready | Choose price and complete declarations before submission | +| App Review | Apple reviewer | Exercise the app without credentials | Review notes and contact checklist | C01, C02, C03, C07 | Support and privacy URLs | Tom Ballard | outputs/app-store/REVIEW_NOTES.md | Owner submits | Ready | Run installed-build smoke test before submission | +| App Privacy | Apple reviewer and users | Understand data handling | Questionnaire guidance and public policy | C02, C04, C05, C07 | docs/privacy.md | Tom Ballard | outputs/app-store/APP_PRIVACY.md | Owner attests in App Store Connect | Ready | Select Data Not Collected | +| Support | Existing users | Resolve setup and review issues | Public support page | C01-C03 | docs/support.md | Tom Ballard | docs/support.md | Public | Ready | Public URL verified with HTTP 200 on 22 July 2026 | diff --git a/launch-pack/CLAIMS_LEDGER.md b/launch-pack/CLAIMS_LEDGER.md index 19f14e2..b53d7cf 100644 --- a/launch-pack/CLAIMS_LEDGER.md +++ b/launch-pack/CLAIMS_LEDGER.md @@ -2,9 +2,10 @@ | ID | Claim | Importance | Evidence | Status | Qualification | Channels | Owner | | --- | --- | --- | --- | --- | --- | --- | --- | -| C01 | Sorting Hat renames and files items using plain-language rules | Required | Shipping app, README product contract, genuine activity capture | Verified | Model decisions are validated; uncertain items remain for review | Listing, screenshots | Tom Ballard | -| C02 | Apple model processing can stay on the Mac | Required | Current Model Settings UI and Foundation Models implementation | Verified | Requires macOS 26, Apple Intelligence, and a supported Mac | Listing, privacy | Tom Ballard | -| C03 | Finder intake does not alter the original | Required | Finder Action copy-only implementation and docs | Verified | The staged copy is then processed by Sorting Hat | Listing, review notes, support | Tom Ballard | +| C01 | Sorting Hat renames and files items using plain-language rules | Required | Shipping app, README product contract, genuine activity capture | Verified | Model decisions are validated; uncertain items remain for review | README, listing, screenshots | Tom Ballard | +| C02 | Apple model processing can stay on the Mac | Required | Current Model Settings UI and Foundation Models implementation | Verified | Requires macOS 26, Apple Intelligence, and a supported Mac | README, listing, privacy | Tom Ballard | +| C03 | Finder intake does not alter the original | Required | Finder Action copy-only implementation and docs | Verified | The staged copy is then processed by Sorting Hat | README, listing, review notes, support | Tom Ballard | | C04 | Sorting Hat does not track users or contain advertising | Required | Privacy manifest and dependency/runtime inspection | Verified | Optional providers receive content only when configured | Privacy, App Privacy | Tom Ballard | | C05 | The Mac App Store app collects no data | Required | Store compile condition, loopback provider policy, focused tests, archive marker, and privacy manifest | Verified | Applies to the Mac App Store build; other distribution channels retain separate provider choices | App Privacy | Tom Ballard | | C06 | Private Cloud Compute is a shipping option | Optional | Current app UI omits PCC and saved settings force it off | Removed | Legacy research and stale README wording are not product capabilities | None | Tom Ballard | +| C07 | The Mac App Store build has no remote model route | Required | Store compile condition, loopback provider policy, focused tests, archive marker, and selected build 2 | Verified | Applies only to the Mac App Store build | README, listing, review notes, privacy | Tom Ballard | diff --git a/launch-pack/HANDOFF.md b/launch-pack/HANDOFF.md index d5ddcb6..ea2a0ac 100644 --- a/launch-pack/HANDOFF.md +++ b/launch-pack/HANDOFF.md @@ -4,20 +4,21 @@ - Release: First Mac App Store submission - Version: 0.1.0 - Build: 2 -- Pack status: Local-only build 2 uploaded, processed, and selected; URL publication and installed verification remain +- Pack status: Local-only build 2 uploaded, processed, and selected; owner declarations and installed verification remain - Publication authority: Tom Ballard only -- Exact next action: Merge PR #30, verify the public support/privacy URLs, then complete the remaining owner declarations +- Exact next action: Choose pricing, complete the remaining owner declarations, and run the installed-build smoke test ## Included deliverables | Channel | Output | Status | Claim IDs | Notes | | --- | --- | --- | --- | --- | -| Mac App Store | outputs/app-store/LISTING.md | Ready | C01-C03 | Copy respects current product behavior | +| Repository | README.md | Ready | C01, C02, C03, C07 | Merge & Tell rewrite is in PR #31 | +| Mac App Store | outputs/app-store/LISTING.md | Ready | C01, C02, C03, C07 | Copy respects current product behavior | | Screenshots | outputs/app-store/screenshots | Ready | C01-C03 | Two opaque 1440×900 JPEGs | | Icon | outputs/app-store/icon/app-icon-1024.png | Ready | None | Reference copy of the icon embedded in selected build 2 | -| App Review | outputs/app-store/REVIEW_NOTES.md | Ready | C01-C03 | Notes and review contact are saved in App Store Connect | -| Privacy | outputs/app-store/APP_PRIVACY.md and docs/privacy.md | Ready | C02, C04, C05 | Build 2 is verified; select Data Not Collected | -| Support | docs/support.md | Ready | C01-C03 | Public URL works only after merge | +| App Review | outputs/app-store/REVIEW_NOTES.md | Ready | C01, C02, C03, C07 | Notes and review contact are saved in App Store Connect | +| Privacy | outputs/app-store/APP_PRIVACY.md and docs/privacy.md | Ready | C02, C04, C05, C07 | Build 2 is verified; select Data Not Collected | +| Support | docs/support.md | Ready | C01-C03 | Public support and privacy URLs return HTTP 200 | ## Omitted or not-applicable deliverables @@ -35,11 +36,11 @@ - Uploaded build 2, confirmed Apple processing state `VALID`, and selected it for version 0.1.0. - Saved the local-only listing, categories, age rating, review contact, and review notes in App Store Connect. +- Confirmed PR #30 merged and both public support/privacy URLs return HTTP 200 on 22 July 2026. - Captured the real app with synthetic files, restored the user's configuration and activity, and removed fixture outputs. - Adapted screenshots proportionally to an accepted Mac size with provenance evidence. - Measured and visually reviewed the 1024 px shipping icon. ## Remaining decisions and blockers -- Merge the support/privacy pages, then verify their public URLs. - Choose price, complete App Privacy, export compliance, and content rights, then run installed-build verification. diff --git a/launch-pack/LAUNCH_BRIEF.md b/launch-pack/LAUNCH_BRIEF.md index 9ddbc22..5774735 100644 --- a/launch-pack/LAUNCH_BRIEF.md +++ b/launch-pack/LAUNCH_BRIEF.md @@ -3,17 +3,17 @@ - Product: Sorting Hat: File Organiser - Release: First Mac App Store submission - Version: 0.1.0 -- Build: 1 -- Release state: Build uploaded and selectable; metadata and owner decisions remain +- Build: 2 +- Release state: Local-only build uploaded, processed, and selected; owner declarations and installed verification remain - Release date or window: After App Review approval -- Authoritative source: PR #30 at commit 06946dd plus App Store Connect build 0.1.0 (1) +- Authoritative source: merged PR #30 at commit 7dcc9ef plus App Store Connect build 0.1.0 (2) ## Audience and outcome - Primary audience: Mac users who want files renamed and organised without writing automation - User outcome: Describe a filing plan once, then send files to one Inbox and see them renamed and routed into useful folders - Launch objective: Submit an accurate, reviewable first Mac App Store version -- Primary call to action: Download Sorting Hat from the Mac App Store +- Primary call to action: At launch, download Sorting Hat from the Mac App Store; before launch, complete the owner submission gates - Canonical destination: App Store Connect record 6792563259 ## Availability and boundaries @@ -22,20 +22,20 @@ - Rollout or eligibility: Public Mac App Store after manual release and review approval - Pricing: Owner decision required; free is recommended for the first submission - Material limitations: Model availability affects sorting; uncertain items require review; Store Ollama connections are restricted to the same Mac -- Required disclosures: Optional provider transmission, folder access, no developer account, and Finder Action copy-only intake +- Required disclosures: same-Mac model boundary, folder access, no developer account, and Finder Action copy-only intake ## Delivery contract -- Included channels: Mac App Store product page, screenshots, icon handoff, privacy policy, support page, App Review notes, submission checklist +- Included channels: repository front door, Mac App Store product page, screenshots, icon handoff, privacy policy, support page, App Review notes, submission checklist - Deliberately omitted channels: Homebrew, Developer ID release, press, social, and demo video - Format or submission constraints: 1-10 opaque 16:10 Mac screenshots; icon embedded in the uploaded build; App Store field limits - Accessibility requirements: Screenshots remain legible at product-page scale and listing copy describes the visual outcome -- Publication authority: Tom Ballard performs all App Store Connect edits and submission actions +- Publication authority: Tom Ballard controls App Review submission and public release ## Evidence summary -- Release artifact or verified build: App Store Connect shows selectable build 0.1.0 (1) with an included App Icon -- Tests and measurements: PR #30 CI is green; Store archive and selected build were previously verified +- Release artifact or verified build: App Store Connect build 0.1.0 (2) passed Apple validation, processed as `VALID`, and is selected with an included App Icon +- Tests and measurements: PR #30 CI is green; Store structural preflight, archive validation, and selected-build verification passed - Specification and acceptance criteria: Issue #29 and docs/distribution.md -- Build logs and decisions: PR #30 history and App Store Connect read-only audit on 20 July 2026 +- Build logs and decisions: merged PR #30 history and App Store Connect API verification on 21 July 2026 - Existing assets and copy: Shipping app icon plus two genuine, sanitised application captures diff --git a/launch-pack/RELEASE_CHECKLIST.md b/launch-pack/RELEASE_CHECKLIST.md index 3390de8..9f275d4 100644 --- a/launch-pack/RELEASE_CHECKLIST.md +++ b/launch-pack/RELEASE_CHECKLIST.md @@ -3,11 +3,11 @@ | Gate | Requirement | Evidence or observation | Result | Owner | Next action | | --- | --- | --- | --- | --- | --- | | Release identity | Version, build, date, and availability agree | App Store record is 0.1.0; local-only build 2 is valid and selected | Ready | Tom Ballard | Preserve build 2 through submission | -| Claims | Every used claim is verified or visibly qualified | Claims ledger removes PCC and blocks no-data claim | Ready | Tom Ballard | Preserve qualifications when entering copy | +| Claims | Every used claim is verified or visibly qualified | Claims ledger removes PCC and verifies the Store-only no-data claim | Ready | Tom Ballard | Preserve channel qualifications in public copy | | Assets | Required outputs exist and open correctly | Icon plus two real screenshots are present in App Store Connect | Ready | Tom Ballard | Visually confirm the final product-page preview | | Technical | Channel and submission constraints pass | Local-only build 2 passed structural preflight and Apple validation; installed-build Store smoke test remains | Blocked | Tom Ballard | Install and smoke-test build 2 | | Accessibility | Captions, contrast, text alternatives, and readability pass | Screens are legible at 1440×900; copy explains outcomes | Ready | Tom Ballard | Review App Store preview at normal scale | | Privacy | No secrets, private data, or embargoed details leak | Store runtime neutralizes OpenAI and non-loopback Ollama; privacy manifest declares no collection | Ready | Tom Ballard | Select Data Not Collected | -| Links | Destinations and calls to action work | Repository links exist; support/privacy pages become public after merge | Blocked | Tom Ballard | Merge PR #30, then verify both public URLs anonymously | +| Links | Destinations and calls to action work | Support and privacy URLs return HTTP 200 from merged `main` | Ready | Tom Ballard | Recheck in the final App Store preview | | Provenance | Required source and model contribution records exist | Per-asset JSON records source hashes and transformations | Ready | Tom Ballard | Retain evidence with the pack | | Authority | Publisher and manual action are explicit | Metadata and build are prepared; App Review submission remains owner-controlled | Ready | Tom Ballard | Owner answers remaining declarations and submits | diff --git a/launch-pack/outputs/app-store/REVIEW_NOTES.md b/launch-pack/outputs/app-store/REVIEW_NOTES.md index a20c26f..5ee6de8 100644 --- a/launch-pack/outputs/app-store/REVIEW_NOTES.md +++ b/launch-pack/outputs/app-store/REVIEW_NOTES.md @@ -25,4 +25,4 @@ The app does not require a network connection when Apple's on-device provider is - Name: Tom Ballard - Email: tom@armytage.co -- Phone: owner entry required in App Store Connect +- Phone: saved in App Store Connect; deliberately not duplicated in the repository diff --git a/launch-pack/outputs/app-store/SUBMISSION_CHECKLIST.md b/launch-pack/outputs/app-store/SUBMISSION_CHECKLIST.md index 77efa32..a6eca49 100644 --- a/launch-pack/outputs/app-store/SUBMISSION_CHECKLIST.md +++ b/launch-pack/outputs/app-store/SUBMISSION_CHECKLIST.md @@ -26,6 +26,6 @@ - Confirm the saved review notes and contact details match `REVIEW_NOTES.md`. - Keep build 0.1.0 (2) selected. -- Verify the public support and privacy URLs anonymously after PR #30 merges. +- Public support and privacy URLs returned HTTP 200 on 22 July 2026; recheck them in the final preview. - Run an installed-build smoke test for first setup, persisted folder access, Apple on-device sorting, manual review, Finder intake, pause/resume, and launch at login. - Submit only after every blocker in `RELEASE_CHECKLIST.md` is resolved by the owner. From 22c4f41454bde3c9e6c2702a3a476972b306fac3 Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Wed, 22 Jul 2026 06:30:36 +0100 Subject: [PATCH 3/4] docs(readme): adopt candid builder voice [issue:#29] Rewrites the full README in a direct builder-operator voice while preserving verified behavior, commands, safety boundaries, evaluation evidence, and channel-specific release status. Makes the user workflow scannable, distinguishes the local-only Store build from source distribution, and keeps limitations visible without leading with a warning. --- README.md | 136 +++++++++++++++++++++++++----------------------------- 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index aa53380..e4880d4 100644 --- a/README.md +++ b/README.md @@ -12,65 +12,44 @@ Build and run from source: ./script/build_and_run.sh ``` -[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) +[See exactly how Sorting Hat extends Apple’s WWDC26 file-sorting demo, including the passing shipping-path benchmark and its limits.](docs/wwdc26-comparison.md) ## Requirements -- macOS 14 or later for the menu-bar app -- macOS 26 plus Apple Intelligence for Apple's native on-device Foundation Model, or [Ollama](https://ollama.com/) with a local model on earlier macOS versions +- macOS 14 or later +- macOS 26, Apple Intelligence, and a supported Mac for Apple’s on-device Foundation Model +- [Ollama](https://ollama.com/) with a local model when Apple’s model is unavailable - Swift 6.2+ to build from source -- XcodeGen 2.44.1 to regenerate the checked-in native app project +- XcodeGen 2.44.1 only when regenerating the checked-in Xcode project -## Try it +## How it works -```sh -swift build -c release -.build/release/sorting-hat init -mkdir -p ~/SortingHat/Inbox -.build/release/sorting-hat once --dry-run -.build/release/sorting-hat watch -``` +One Inbox. One output root. The model proposes; Swift decides. -## Menu-bar app +1. Add a file in the app or send it through Finder’s **Send to Sorting Hat** Quick Action. +2. Sorting Hat extracts useful text, asks the selected model for a filename, tags, and destination, then validates the answer. +3. Safe decisions move to a rule-specific folder beneath your output root. Missing folders are created automatically. +4. Uncertain or invalid decisions stay in the Inbox for review. No guessing, no mystery pile called `Sorted` inside the Inbox. -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. +The Inbox is intake-only. Sorting Hat preserves file extensions, rejects absolute paths and traversal, protects existing files with numbered names, and refuses a “rename” that leaves the original filename unchanged. Controlled `Put ... in ...` rules compile into an allow-list, so free-form model output cannot invent a destination outside the ruleset. -Build and open the native companion with: +## The Mac app -```sh -./script/build_and_run.sh -``` +The dashboard is the single place to see the Inbox, Activity, rules, and anything that needs attention. Left-click the menu-bar hat to open or close the dashboard. Right-click it for quick actions such as pause, resume, and sort now. + +Sorting Hat watches while it is running, shows exactly how each file was renamed and filed, supports undo and manual recovery, and uses macOS’s native launch-at-login service. + +### Send to Sorting Hat from Finder + +Signed builds include a first-party **Send to Sorting Hat** Quick Action. Select files in Finder, then choose **Quick Actions → Send to Sorting Hat**. The action copies each file into Sorting Hat’s App Group queue. It never changes the original. + +Paused apps still import files into the Inbox. Closed apps pick up staged files on the next launch. One invocation accepts up to 256 files, with a 256 MB per-file limit, a 1 GB selection limit, and a 25-second deadline. Partial failures name the files that were not queued instead of pretending the whole batch worked. + +Enable the action in **System Settings → General → Login Items & Extensions → Finder**. If Finder caches the old extension state, relaunch Finder once. **Sorting Hat Settings → Finder** shows integration health, permission repair, queued copies, failures, and **Retry** or **Remove** actions. Older Automator actions with the same name can also be migrated safely. The full contract is in the [Finder Quick Action architecture notes](docs/finder-quick-action.md). + +## Rules and configuration -### Finder Quick Action - -Signed builds include a first-party **Send to Sorting Hat** Finder Quick -Action. Select files in Finder, then choose **Quick Actions → Send to Sorting -Hat**. This is copy-only intake: originals stay exactly where they are while -the extension stages durable copies in Sorting Hat's App Group queue. Running -apps normally drain that queue within a second; paused apps still import into -the Inbox but do not sort; closed apps import the staged files on next launch. -One invocation accepts up to 256 files and loads File Provider items -sequentially. The Finder transport has a 256 MB per-file limit, a 1 GB -selection limit, and a 25-second request deadline; anything left unqueued is -named explicitly as a partial failure and can still be added from inside -Sorting Hat. - -Enable the action in **System Settings → General → Login Items & Extensions → -Finder**. macOS offers no reliable public API for checking whether the action -is enabled, so confirm the installed build by invoking it from Finder's -right-click **Quick Actions** menu. Finder can cache extension availability; -if the newly enabled action is missing, relaunch Finder once before diagnosing -registration. Older Sorting Hat installs may also have an Automator action -with the same name that masks the native item. **Sorting Hat Settings → -Finder** provides a reversible migration path that preserves the workflow as a -backup until the native delivery is verified. Integration health, Inbox permission -repair, queued copies, failures, and **Retry** or **Remove** recovery actions -are available under **Sorting Hat Settings → Finder**. See the [native Finder -Quick Action architecture and migration notes](docs/finder-quick-action.md). - -It watches automatically, shows recent filing activity, opens the Inbox and rules file, can pause or sort immediately, and uses macOS's native launch-at-login service. The dashboard appears at launch and the graduation-cap menu remains available for quick actions. - -Drag files into `~/SortingHat/Inbox`. Keep `sortinghat.conf` in the directory where you launch the command, or pass `--config /path/to/sortinghat.conf`. +Rules are plain language, editable, and ordered from specific routes to the catch-all: ```yaml inbox: ~/SortingHat/Inbox @@ -92,21 +71,27 @@ rules: - Put everything else in Files/YYYY-MM and add one useful topic tag. ``` -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. +Keep `sortinghat.conf` in the directory where you launch the CLI, or pass `--config /path/to/sortinghat.conf`. -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`. +## Models and privacy -Sorting Hat reads searchable PDFs, plain-text formats, RTF, Word, and OpenDocument files. For scanned PDFs and receipt images, it uses Apple's local Vision framework to recognize text before asking the selected model to name and file the document. Embedded PDF text is preferred, so searchable PDFs avoid unnecessary OCR. Extraction is limited to the first 5 pages and 12,000 characters; the source file is never modified. If a scanned PDF cannot be rendered or contains no sufficiently confident text, Sorting Hat leaves it in the Inbox and reports the extraction failure. +Source and Developer ID builds offer **Automatic**, **Apple**, **Ollama**, and **OpenAI**. Automatic mode prefers Apple’s in-process Foundation Models framework and can fall back to a provider you configured. OpenAI keys live in macOS Keychain in the app; the CLI reads `OPENAI_API_KEY`. -When Apple's on-device model is selected, each file is analyzed through an isolated native Foundation Models guided-generation request. Every decision is independently validated before any move, and one failed file does not prevent other Inbox items from being processed. Image and scanned-document text is extracted locally with Vision before inference. Sorting Hat deliberately favours reliable per-file naming and review decisions over an unverified native batching throughput claim; the legacy PCC research adapter retains bounded batching. +The Mac App Store build is stricter. It offers Apple’s on-device model and Ollama only on `localhost`, `127.0.0.1`, or `::1`. It contains no OpenAI route, no LAN or remote Ollama route, and no Private Cloud Compute option. Content-extraction failures, unsafe paths, and invalid decisions never trigger a silent cloud escalation. -The Inbox is intake-only. Sorting Hat renames each file and moves it to a rule-specific folder under `output` (for example, `~/SortingHat/Receipts/2026`). For the rule builder's controlled `Put ... in ...` routes, deterministic Swift code canonicalises the model's proposal against configured destinations and rejects unknown or unresolved folders. When at least one controlled route is present, its compiled destinations form the authoritative allow-list for that ruleset; other prose can still guide naming, tagging, and classification but cannot introduce a new destination. Sorting Hat creates destination folders as needed, rejects absolute paths and traversal, preserves the source file type, protects existing files with numbered names, and writes tags as Finder metadata. +For Apple’s model, `apple_use_case: content-tagging` enables the framework’s content-tagging specialisation. `apple_guardrails: permissive-content-transformations` relaxes content-transformation guardrails for filing material the default policy refuses; keep the default unless your rules need the exception. Apple requests use in-process guided generation and greedy sampling. -If a model returns the uploaded filename unchanged, Sorting Hat leaves the source in the Inbox and reports an invalid sorting decision instead of silently skipping the requested rename. +Private Cloud Compute remains a research target for the macOS 27 toolchain. It is not a shipping feature. -If the model cannot classify a file confidently, it can return an empty destination. Sorting Hat treats that as **Needs review** and leaves the source in the Inbox rather than guessing. Automatic provider selection also falls through to a configured Ollama or OpenAI provider when Apple reports the system model as available but generation fails. +## Documents and OCR -## Commands +Sorting Hat reads searchable PDFs, plain text, RTF, Word, and OpenDocument files. Apple’s Vision framework extracts text locally from scanned PDFs, receipts, and screenshots before inference. Searchable PDF text wins over OCR, which keeps the common path faster and simpler. + +Extraction is capped at the first five pages and 12,000 characters. The source file is never modified during analysis. If a scan cannot be rendered or does not contain confident text, it stays in the Inbox with a visible extraction failure. + +Each file gets an isolated model request. One failure does not block the rest of the Inbox. Sorting Hat favours reliable per-file naming and review over an unverified batching speed claim. + +## CLI ```text sorting-hat init [--config PATH] @@ -115,9 +100,21 @@ sorting-hat watch [--config PATH] [--dry-run] sorting-hat evaluate --live --corpus PATH --output PATH [--baseline PATH] [--config PATH] ``` -## Live Foundation Models evaluation +Build and try the CLI: + +```sh +swift build -c release +.build/release/sorting-hat init +mkdir -p ~/SortingHat/Inbox +.build/release/sorting-hat once --dry-run +.build/release/sorting-hat watch +``` + +`watch` uses a small polling loop. That is intentionally boring and reliable for a human-scale drop folder; an event-driven watcher can come later. -Live quality checks are deliberately opt-in and read-only. Create a private, anonymized corpus outside the repository, copy the format in `Tests/SortingHatTests/Fixtures/live-evaluation-corpus.example.json`, then run: +## Quality, measured + +The live evaluator runs the same extractor, routing policy, and validator as the shipping app without moving source files. Use a private, anonymised corpus outside the repository and copy the schema from `Tests/SortingHatTests/Fixtures/live-evaluation-corpus.example.json`: ```sh .build/debug/sorting-hat evaluate --live \ @@ -126,17 +123,11 @@ Live quality checks are deliberately opt-in and read-only. Create a private, ano --config sortinghat.conf ``` -The manifest is versioned and contains rules plus cases with a relative source path, content kind (`receipt`, `scan`, `screenshot`, `pdf`, `office_document`, or `ambiguous`), accepted folders, required filename terms, required tags, and whether an empty-folder abstention is expected. The checked-in template defines all six required classes but deliberately includes no documents. Corpus files must sit beside or below the manifest; output inside that corpus directory is rejected. System-model evaluation calls the same native Foundation Models analyzer as the app but never calls `Organizer.apply`, so sources are not renamed, tagged, or moved. PCC research retains the command-line adapter until the macOS 27 native PCC API can be compiled and validated with Xcode 27. - -Each run writes `evaluation.json` and `summary.md`. In schema-v2 artifacts, `raw_decision` is the model's direct proposal before deterministic routing policy, while `decision` is the resolved and validated shipping-path result that the evaluator scores. The artifact also records model, OS, prompt and routing-policy versions, validation errors, pre-validation decision latency, accuracy, generation failures, unsafe/invalid decisions, and abstentions. Latency stops after analysis and deterministic routing resolution, before the separate `Organizer` validation pass. Pass a schema-compatible previous `evaluation.json` with `--baseline` to expose regressions; incompatible schemas, corpora, prompts, routing policies, or model environments are reported instead of being compared silently. Exit status `2` means a threshold or baseline check failed; `1` means the evaluation could not run. Do not commit corpus documents or results containing private or copyrighted content; only the synthetic manifest example belongs in the repository. - -The completed Issue #23 result is published in [`evaluation/ROUTING_RESULTS.md`](evaluation/ROUTING_RESULTS.md): routing policy v1 scored 108/108 exact decisions across nine final runs, with 18/18 ambiguous cases held for review, zero invalid final decisions, and an 8.1% recorded pre-validation latency increase against the corrected shipping-path baseline. The 12-case private corpus is a regression gate, not a universal accuracy claim. - -For reproducible prompt, system/content-tagging, and system/PCC research, use the locked standalone Python project in [`evaluation/`](evaluation/README.md). It consumes the same corpus manifest and produces JSON, CSV, Markdown, and chart artifacts without adding Python to the application runtime. The Swift live evaluator remains the product-quality authority because it exercises the shipping extractor, routing policy, and validator. +Each run writes `evaluation.json` and `summary.md`, including the raw model proposal, final validated decision, environment, latency, accuracy, failures, invalid decisions, and abstentions. Pass a compatible previous artifact with `--baseline` to expose regressions. Exit status `2` means a quality threshold or baseline check failed; `1` means the evaluation could not run. -Bounded tool-calling candidates are also evaluated there under a documented threat model and evidence gate. Filesystem mutation remains exclusively in validated Swift code; no evaluation tool is available to the shipping app unless a future change demonstrates and reviews measurable value first. +The completed Issue #23 gate scored 108/108 exact final decisions across nine runs, held all 18 ambiguous cases for review, and produced zero invalid final decisions. It also recorded an 8.1% pre-validation latency increase against the corrected baseline. That is the honest result: routing passed; speed did not improve. Read the method and boundaries in [`evaluation/ROUTING_RESULTS.md`](evaluation/ROUTING_RESULTS.md). -`watch` intentionally uses a small polling loop in this first version. It is simple and reliable for a human-scale drop folder; a launch agent and event-driven watcher can come next. +The 12-case private corpus is a regression gate, not a universal accuracy claim. Do not commit corpus documents or results containing private or copyrighted content. The standalone [`evaluation/`](evaluation/README.md) project remains available for prompt, system/content-tagging, PCC research, and bounded tool-calling experiments; none of those research tools can mutate the shipping filesystem path. ## Development @@ -148,12 +139,13 @@ swift test ./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. +Inference sits behind `FileAnalyzing`, so filesystem safety can be tested without a live model. The Foundation Models decision path can also be shared by a future iPhone or iPad client, but iOS cannot behave like a continuously watched Mac folder. The [iOS client boundary](docs/ios-client-architecture.md) documents what is reusable and what needs a Files or Share-extension workflow. -The Foundation Models decision path can be shared by a future iPhone or iPad client, but iOS intake cannot behave like a continuously watched Mac folder. See the explicit [iOS client boundary](docs/ios-client-architecture.md) for the reusable layers and the required Files/Share-extension architecture. +## Distribution status -## Release status +There are two release tracks. They are deliberately separate. -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). +- **Mac App Store:** local-only build `0.1.0 (2)` passed Apple validation, processed as `VALID`, and is selected in App Store Connect. It has not been submitted for review or published. Pricing, App Privacy, export compliance, content rights, and installed-build verification still need owner sign-off. +- **GitHub and Homebrew:** the downloadable `v0.1.0` artifact is an experimental, ad-hoc-signed pre-release. It is not Developer ID signed or notarised, so Gatekeeper may block it. Issue [#24](https://github.com/tcballard/SortingHat/issues/24) tracks the signed and notarised release path. -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). +Issue [#29](https://github.com/tcballard/SortingHat/issues/29) tracks the remaining App Store work. The [distribution guide](docs/distribution.md), [privacy policy](docs/privacy.md), and [support page](docs/support.md) hold the channel-specific details. From a6c3f00eda339bd838ce8def8feed2bc80820e27 Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Wed, 22 Jul 2026 06:39:11 +0100 Subject: [PATCH 4/4] fix(release): stabilize Store policy preflight [issue:#29] Avoid a pipefail false negative when grep exits after finding a policy string and closes the strings pipeline early. Capture binary strings once, then run the forbidden-path and required-policy assertions against the completed output. --- script/preflight_app_store.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/script/preflight_app_store.sh b/script/preflight_app_store.sh index 6e6cb82..b89c4e4 100755 --- a/script/preflight_app_store.sh +++ b/script/preflight_app_store.sh @@ -58,12 +58,15 @@ if not any( raise SystemExit("Compiled asset catalog is missing the 512pt @2x AppIcon rendition.") PY -if strings "$APP_BINARY" | grep -Fq "/usr/bin/fm"; then +BINARY_STRINGS="$DERIVED_DATA/app-store-binary-strings.txt" +strings "$APP_BINARY" > "$BINARY_STRINGS" + +if grep -Fq "/usr/bin/fm" "$BINARY_STRINGS"; then echo "App Store binary unexpectedly contains the legacy fm executable path." >&2 exit 1 fi -if ! strings "$APP_BINARY" | grep -Fq "The Mac App Store build can connect only to Ollama running on this Mac"; then +if ! grep -Fq "The Mac App Store build can connect only to Ollama running on this Mac" "$BINARY_STRINGS"; then echo "App Store binary is missing the local-only provider policy." >&2 exit 1 fi