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.

MistakeWhat HappenedPrevention
Guessed APIAssumed API exists. It doesn't. 20 min wasted.verify_api first
Skipped xcodegenCreated file, "file not found" for 20 minutesxcodegen generate after new files
Kept guessingSame error 4 times. Finally checked apple-docs MCP.Stop at 2, investigate
Deleted "unused" filePeriphery said unused, but ServiceContainer needed itGrep before delete
Extension not loadingChanged extension target but didn't rebuild host appRebuild both targets
Finder menu staleCached menu items from previous sessionDon't cache menus - rebuild on each menu(for:) call
App Group mismatchExtension couldn't read shared dataVerify 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 bugOrientation + 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:

  1. Read Rule #0 first (Section "The Rules") - It's about HOW to use all other rules
  2. All files stay in project - NEVER write files outside ~/SaneApps/apps/SaneClick/ unless user explicitly requests it
  3. Use SaneMaster.rb for build/test - ./scripts/SaneMaster.rb verify, never raw xcodebuild
  4. 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:

  • FIFinderSync API (Finder Sync Extension)
  • NSFileProviderExtension callbacks
  • 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

LinesStatus
<500Good
500-800OK if single responsibility
>800Must 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
ScoreMeaning
9-10All rules followed
7-8Minor miss
5-6Notable gaps
1-4Multiple 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)

ToolPurposeWhen to Use
Task agentsExplore codebase, analyze patterns"Where is X used?", "How does Y work?"
apple-docs MCPVerify Apple APIs exist and usageAny Apple framework API, especially FIFinderSync
context7 MCPLibrary documentationThird-party packages
WebSearch/WebFetchSolutions, patterns, best practicesError messages, architectural questions
Grep/Glob/ReadLocal investigationFind similar patterns, check implementations
AgentMemoryShared bug patterns and architecture decisions"Have we seen this before?"
RESEARCH.mdProject-specific API researchFinder 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

ConditionThresholdMeaning
Same error 3x3 identicalStuck in loop, repeating same mistake
Total failures5 any errorsFlailing, 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:

AspectRequirement
Bundle locationExtension bundle MUST be inside host app bundle
App GroupM78L6FXD48.group.com.saneclick.app for shared storage
User enablementUsers must manually enable in System Settings > Extensions > Finder
Menu cachingNEVER cache menus - rebuild NSMenu on each menu(for:) call
Script executionExtension sends notification → host app executes script

Status debugging:

  • Use pluginkit and a real Finder right-click probe as the source of truth for extension enablement.
  • Do not trust FIFinderSyncController.isExtensionEnabled by 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:

  1. Build both targets (host app + extension)
  2. Launch host app
  3. Ensure extension is enabled in System Settings
  4. Right-click files in Finder to see context menu

Key Components

ComponentLocationPurpose
FinderSyncSaneClickExtension/FinderSync.swiftProvides context menu items in Finder
ScriptStoreSaneClick/Services/ScriptStore.swiftManages script configurations
ScriptExecutorSaneClick/Services/ScriptExecutor.swiftRuns scripts safely
ContentViewSaneClick/Views/ContentView.swiftMain settings UI

Troubleshooting

ProblemFix
Ghost beeps / no launchxcodegen generate
Phantom build errors./scripts/SaneMaster.rb clean then rebuild
"File not found" after new filexcodegen generate
Tests failing mysteriouslyClean build, then test
Extension not showing in FinderCheck System Settings > Extensions > Finder
Stale context menuRebuild both targets, restart Finder (killall Finder)
App Group data not syncingVerify entitlements match in both targets
Extension crashes on launchCheck 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:

  1. 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 / NSBitmapImageRep round-trip, with the extension matching the bytes. Dimensions, file-exists, type, and counts all pass on garbage output.
  2. 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.
  3. Blank-output guard. Explicitly assert the output is not all-one-color / all-transparent / zero-information, so a blank render fails the test.
  4. Real-artifact visual check before ship. Headless tests and the customer_ui_sweep don'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

  1. Run ./scripts/SaneMaster.rb bootstrap (if available) or ./scripts/SaneMaster.rb doctor
  2. Read RESEARCH.md if unfamiliar with Finder Sync API
  3. Search AgentMemory with mcp__agentmemory__memory_smart_search query: "SaneClick"
  4. Kill stale processes: killall SaneClick 2>/dev/null || true
  5. 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_preflight before submission.