SaneClick Development Guide (SOP)
July 15, 2026 · View on GitHub
README · ARCHITECTURE · DEVELOPMENT · PRIVACY · SECURITY
Version 1.0 | Last updated: 2026-02-02
SINGLE SOURCE OF TRUTH for all Developers and AI Agents.
Sane Philosophy
┌─────────────────────────────────────────────────────┐
│ BEFORE YOU SHIP, ASK: │
│ │
│ 1. Does this REDUCE fear or create it? │
│ 2. Power: Does user have control? │
│ 3. Love: Does this help people? │
│ 4. Sound Mind: Is this clear and calm? │
│ │
│ Grandma test: Would her life be better? │
│ │
│ "Not fear, but power, love, sound mind" │
│ — 2 Timothy 1:7 │
└─────────────────────────────────────────────────────┘
→ Full philosophy: ~/SaneApps/meta/Brand/NORTH_STAR.md
This Has Burned You
Real failures from past sessions. Don't repeat them.
| Mistake | What Happened | Prevention |
|---|---|---|
| Guessed API | Assumed API exists. It doesn't. 20 min wasted. | verify_api first |
| Skipped xcodegen | Created file, "file not found" for 20 minutes | xcodegen generate after new files |
| Kept guessing | Same error 4 times. Finally checked apple-docs MCP. | Stop at 2, investigate |
| Deleted "unused" file | Periphery said unused, but ServiceContainer needed it | Grep before delete |
| Extension not loading | Changed extension target but didn't rebuild host app | Rebuild both targets |
| Finder menu stale | Cached menu items from previous session | Don't cache menus - rebuild on each menu(for:) call |
| App Group mismatch | Extension couldn't read shared data | Verify M78L6FXD48.group.com.saneclick.app in both targets |
| Tested the shape, not the content | "Rotate 90°" shipped a blank image past green tests — the test only asserted output dimensions (correct), never read a pixel. Same family: OCR returned empty on portrait photos while green. Class: green tests, broken output. | For any action that PRODUCES output, assert the actual CONTENT: a known pixel color at a known coordinate (proves non-blank AND direction), decoded text == expected, real format via magic-bytes / NSBitmapImageRep round-trip with extension matching the bytes. Never just size/count/exists. |
| Happy-path fixtures hid the bug | Orientation + format bugs slipped through because every fixture was an upright PNG (orientation=1). | Use adversarial fixtures: EXIF-oriented / portrait (orientation ≠ 1) images, non-PNG/JPEG sources (HEIC, TIFF), and multi-file batches with one unreadable file. Then visually verify the real artifact on a real input before claiming done. |
The #1 differentiator: Skimming this SOP = 5/10 sessions. Internalizing it = 8+/10.
"If you skim you sin." — The answers are here. Read them.
Quick Start for AI Agents
New to this project? Start here:
- Read Rule #0 first (Section "The Rules") - It's about HOW to use all other rules
- All files stay in project - NEVER write files outside
~/SaneApps/apps/SaneClick/unless user explicitly requests it - Use SaneMaster.rb for build/test -
./scripts/SaneMaster.rb verify, never rawxcodebuild - Self-rate after every task - Rate yourself 1-10 on SOP adherence (see Self-Rating section)
If bootstrap fails, run ./scripts/SaneMaster.rb doctor.
Key Commands:
./scripts/SaneMaster.rb verify # Build + test
./scripts/SaneMaster.rb test_mode # Kill → Build → Launch → Logs
./scripts/SaneMaster.rb launch # Launch app
./scripts/SaneMaster.rb logs --follow # Stream logs
The Rules
#0: NAME THE RULE BEFORE YOU CODE
✅ DO: State which rules apply before writing code ❌ DON'T: Start coding without thinking about rules
RIGHT: "Uses Apple API → Rule #2: VERIFY BEFORE YOU TRY"
RIGHT: "New file → Rule #9: NEW FILE? GEN THAT PILE"
WRONG: "Let me just code this real quick..."
#1: STAY IN YOUR LANE
✅ DO: Save all files inside ~/SaneApps/apps/SaneClick/
❌ DON'T: Create files outside project without asking
#2: VERIFY BEFORE YOU TRY
✅ DO: Check apple-docs MCP before using any Apple API ❌ DON'T: Assume an API exists from memory or web search
Especially important for:
FIFinderSyncAPI (Finder Sync Extension)NSFileProviderExtensioncallbacks- App Group / shared container APIs
#3: TWO STRIKES? INVESTIGATE
✅ DO: After 2 failures → stop, follow Research Protocol (see section below) ❌ DON'T: Guess a third time without researching
#4: GREEN MEANS GO
✅ DO: Fix all test failures before claiming done ❌ DON'T: Ship with failing tests
#5: SANEMASTER OR DISASTER
✅ DO: Use ./scripts/SaneMaster.rb for all build/test operations
❌ DON'T: Use raw xcodebuild commands
#6: BUILD, KILL, LAUNCH, LOG
✅ DO: Run full sequence after every code change ❌ DON'T: Skip steps or assume it works
# Kill any running instance
killall SaneClick 2>/dev/null || true
# Build and run
./scripts/SaneMaster.rb test_mode
#7: NO TEST? NO REST
✅ DO: Every bug fix gets a test that verifies the fix
❌ DON'T: Use placeholder or tautology assertions (#expect(true))
#8: BUG FOUND? WRITE IT DOWN
✅ DO: Document bugs in TodoWrite immediately ❌ DON'T: Try to remember bugs or skip documentation
#9: NEW FILE? GEN THAT PILE
✅ DO: Run xcodegen generate after creating any new file
❌ DON'T: Create files without updating project
#10: FIVE HUNDRED'S FINE, EIGHT'S THE LINE
| Lines | Status |
|---|---|
| <500 | Good |
| 500-800 | OK if single responsibility |
| >800 | Must split |
#11: TOOL BROKE? FIX THE YOKE
✅ DO: If SaneMaster fails, fix the tool/config ❌ DON'T: Work around broken tools
#12: TALK WHILE I WALK
✅ DO: Use subagents for heavy lifting, stay responsive to user ❌ DON'T: Block on long operations
Self-Rating (MANDATORY)
After each task, rate yourself. Format:
**Self-rating: 7/10**
✅ Used apple-docs MCP, ran full cycle
❌ Forgot to run xcodegen after new file
| Score | Meaning |
|---|---|
| 9-10 | All rules followed |
| 7-8 | Minor miss |
| 5-6 | Notable gaps |
| 1-4 | Multiple violations |
Research Protocol (STANDARD)
This is the standard protocol for investigating problems. Used by Rule #3, Circuit Breaker, and any time you're stuck.
Tools to Use (ALL of them)
| Tool | Purpose | When to Use |
|---|---|---|
| Task agents | Explore codebase, analyze patterns | "Where is X used?", "How does Y work?" |
| apple-docs MCP | Verify Apple APIs exist and usage | Any Apple framework API, especially FIFinderSync |
| context7 MCP | Library documentation | Third-party packages |
| WebSearch/WebFetch | Solutions, patterns, best practices | Error messages, architectural questions |
| Grep/Glob/Read | Local investigation | Find similar patterns, check implementations |
| AgentMemory | Shared bug patterns and architecture decisions | "Have we seen this before?" |
| RESEARCH.md | Project-specific API research | Finder Sync Extension API, state machine |
Research Output → Plan
After research, present findings in this format:
## Research Findings
### What I Found
- [Tool used]: [What it revealed]
- [Tool used]: [What it revealed]
### Root Cause
[Clear explanation of why the problem occurs]
### Proposed Fix
[Rule #X: NAME] - specific action
[Rule #Y: NAME] - specific action
...
### Verification
- [ ] `./scripts/SaneMaster.rb verify` passes
- [ ] Manual test: [specific check]
Circuit Breaker Protocol
The circuit breaker is an automated safety mechanism that blocks Edit/Bash/Write tools after repeated failures.
When It Triggers
| Condition | Threshold | Meaning |
|---|---|---|
| Same error 3x | 3 identical | Stuck in loop, repeating same mistake |
| Total failures | 5 any errors | Flailing, time to step back |
Recovery Flow
CIRCUIT BREAKER TRIPS
│
▼
┌─────────────────────────────────────────────┐
│ 1. READ ERRORS │
│ Review what failed │
├─────────────────────────────────────────────┤
│ 2. RESEARCH (use ALL tools above) │
│ - What API am I misusing? │
│ - Has this bug pattern happened before? │
│ - What does the documentation say? │
│ - Check RESEARCH.md for Finder Sync │
├─────────────────────────────────────────────┤
│ 3. PRESENT SOP-COMPLIANT PLAN │
│ - State which rules apply │
│ - Show what research revealed │
│ - Propose specific fix steps │
├─────────────────────────────────────────────┤
│ 4. USER APPROVES PLAN │
└─────────────────────────────────────────────┘
│
▼
EXECUTE APPROVED PLAN
Key insight: Being blocked is not failure—it's the system working. The research phase often reveals the root cause that guessing would never find.
Plan Format (MANDATORY)
Every plan must cite which rule justifies each step. No exceptions.
Format: [Rule #X: NAME] - specific action with file:line or command
DISAPPROVED PLAN
## Plan: Fix Bug
### Steps
1. Clean build
2. Fix the issue
3. Rebuild and verify
Approve?
Why rejected:
- No
[Rule #X]citations - can't verify SOP compliance - No tests specified (violates Rule #7)
- Vague "fix" without file:line references
APPROVED PLAN
## Plan: Fix [Bug Description]
### Bugs to Fix
| Bug | File:Line | Root Cause |
|-----|-----------|------------|
| [Description] | [File.swift:50] | [Root cause] |
### Steps
[Rule #5: SANEMASTER OR DISASTER] - Clean build with `./scripts/SaneMaster.rb clean`
[Rule #7: TESTS FOR FIXES] - Create tests:
- Tests/[TestFile].swift: `test[FeatureName]()`
[Rule #6: FULL CYCLE] - Verify fixes:
- `./scripts/SaneMaster.rb verify`
- `killall SaneClick`
- `./scripts/SaneMaster.rb test_mode`
- Manual: [specific check]
[Rule #4: GREEN BEFORE DONE] - All tests pass before claiming complete
Approve?
Project Structure
SaneClick/
├── SaneClick/ # Host app (settings UI)
│ ├── App/ # App entry, AppDelegate
│ ├── Models/ # Script, Category models
│ ├── Services/ # ScriptExecutor, ScriptStore
│ └── Views/ # SwiftUI views
├── SaneClickExtension/ # Finder Sync Extension (CRITICAL)
│ ├── FinderSync.swift # FIFinderSync subclass
│ └── Info.plist # Extension config
├── Tests/ # Unit tests
├── Resources/ # Assets, entitlements
├── docs/ # Cloudflare Pages, appcast
├── RESEARCH.md # All research + state machine
├── project.yml # XcodeGen config
└── CLAUDE.md # Quick reference
Finder Sync Extension Notes
Critical implementation details:
| Aspect | Requirement |
|---|---|
| Bundle location | Extension bundle MUST be inside host app bundle |
| App Group | M78L6FXD48.group.com.saneclick.app for shared storage |
| User enablement | Users must manually enable in System Settings > Extensions > Finder |
| Menu caching | NEVER cache menus - rebuild NSMenu on each menu(for:) call |
| Script execution | Extension sends notification → host app executes script |
Status debugging:
- Use
pluginkitand a real Finder right-click probe as the source of truth for extension enablement. - Do not trust
FIFinderSyncController.isExtensionEnabledby itself; it has reported false for enabled extensions. - Do not auto-open System Settings on launch unless there is fresh evidence the extension is actually disabled and user action is required.
Testing the extension:
- Build both targets (host app + extension)
- Launch host app
- Ensure extension is enabled in System Settings
- Right-click files in Finder to see context menu
Key Components
| Component | Location | Purpose |
|---|---|---|
| FinderSync | SaneClickExtension/FinderSync.swift | Provides context menu items in Finder |
| ScriptStore | SaneClick/Services/ScriptStore.swift | Manages script configurations |
| ScriptExecutor | SaneClick/Services/ScriptExecutor.swift | Runs scripts safely |
| ContentView | SaneClick/Views/ContentView.swift | Main settings UI |
Troubleshooting
| Problem | Fix |
|---|---|
| Ghost beeps / no launch | xcodegen generate |
| Phantom build errors | ./scripts/SaneMaster.rb clean then rebuild |
| "File not found" after new file | xcodegen generate |
| Tests failing mysteriously | Clean build, then test |
| Extension not showing in Finder | Check System Settings > Extensions > Finder |
| Stale context menu | Rebuild both targets, restart Finder (killall Finder) |
| App Group data not syncing | Verify entitlements match in both targets |
| Extension crashes on launch | Check Console.app for crash logs |
Testing Strategy
All interactive elements need accessibility identifiers:
.accessibilityIdentifier("addScriptButton")
.accessibilityIdentifier("scriptList")
.accessibilityIdentifier("saveButton")
If automation can't find it → UX is broken → fix design first.
Verifying output-producing actions (image / PDF / text / file)
A passing test suite is NOT enough for any action that emits an artifact. These bugs hide in the content, which structural assertions never inspect. Required:
- Content, not shape. Assert a known pixel color at a known coordinate
(catches blank/transparent renders AND proves rotate/flip direction), decoded
text == expected (not merely non-empty), and the real file format via
magic-bytes /
NSBitmapImageRepround-trip, with the extension matching the bytes. Dimensions, file-exists, type, and counts all pass on garbage output. - Adversarial fixtures. Include the failure modes, not just the happy path: EXIF-oriented / portrait images (orientation ≠ 1), non-PNG/JPEG sources (HEIC, TIFF), and multi-file batches containing one unreadable file.
- Blank-output guard. Explicitly assert the output is not all-one-color / all-transparent / zero-information, so a blank render fails the test.
- Real-artifact visual check before ship. Headless tests and the
customer_ui_sweepdon't render real pixels or the real Finder menu. Run the action on a real input (live App-Store-sandbox / Finder) and have a human or vision agent inspect the actual output. (Same rule that surfaced the buried OCR submenu in 1.2.1 and the blank rotate in the image port.)
Session Start Checklist
- Run
./scripts/SaneMaster.rb bootstrap(if available) or./scripts/SaneMaster.rb doctor - Read
RESEARCH.mdif unfamiliar with Finder Sync API - Search AgentMemory with
mcp__agentmemory__memory_smart_search query: "SaneClick" - Kill stale processes:
killall SaneClick 2>/dev/null || true - Use subagents for heavy work, verify their output
App Store Review Notes
- Review notes must point reviewers to the actual Pro entry points visible in the build.
- App Store builds must not expose external purchase, GitHub Sponsors, crypto donation, or non-Store unlock paths.
- If shared SaneUI About/license content changes, rerun
SaneMaster.rb appstore_preflightbefore submission.