scansnap-bridge
# ScanSnap Bridge
What this skill does
# ScanSnap Bridge
Auto-process paper scans from the Fujitsu ScanSnap iX1500 into Idan's Obsidian vault via a Claude Code subprocess.
## Flow
```
iX1500 (tap touchscreen) → ScanSnap Home profile → ~/scansnap-inbox/*.pdf
│
▼
scansnap_bridge.py (daemon)
│
├─ wait for file stability (polling)
├─ archive source → ~/ScanArchive/YYYY/MM/ (immutable)
├─ OCR derivative → extract text (pdftotext)
├─ spawn `claude -p` with prompts/classify.md
├─ parse JSON filing plan
├─ execute filing actions against vault
└─ Slack DM summary
```
The **source PDF is never modified**. OCR is only used to extract text for classification; the archive copy remains exactly what came out of the scanner.
## Install
```bash
bash ~/.claude/skills/scansnap-bridge/install.sh
```
Installs `ocrmypdf`, `tesseract`, `poppler` via Homebrew; creates `~/scansnap-inbox/` and `~/ScanArchive/`; writes a launchd plist to `~/Library/LaunchAgents/`.
## Run
```bash
# foreground (for debugging)
python3 ~/.claude/skills/scansnap-bridge/scansnap_bridge.py
# as a persistent launchd agent
launchctl load ~/Library/LaunchAgents/com.idanbeck.scansnap-bridge.plist
# stop
launchctl unload ~/Library/LaunchAgents/com.idanbeck.scansnap-bridge.plist
# one-shot test against an arbitrary PDF
python3 ~/.claude/skills/scansnap-bridge/scansnap_bridge.py once /path/to/test.pdf
# status
python3 ~/.claude/skills/scansnap-bridge/scansnap_bridge.py status
```
Logs: `~/.claude/skills/scansnap-bridge/bridge.log`.
## ScanSnap Home profile config
Minimize ScanSnap Home's role to "driver that drops PDFs in a folder":
1. Open ScanSnap Home → Preferences → disable cloud sync, analytics, notifications, auto-launch on scan completion.
2. Create a profile (edit on the touchscreen if preferred):
- **Name:** `Vault Scan`
- **Save to folder:** `~/scansnap-inbox`
- **File format:** PDF
- **OCR:** OFF (we do our own OCR)
- **Image quality:** Best or Excellent
- **Color mode:** Auto
- **Paper size:** Auto (lets ADF handle mixed sizes)
- **Feed:** Duplex (catches back sides automatically)
- **Rotation:** Auto
- **Blank page removal:** ON
- **File name:** `{datetime}` — timestamp-based so we never collide
3. Delete every other profile so the touchscreen shows only `Vault Scan`.
4. ScanSnap Home's auto-organize, auto-tagging, cloud upload, business card extraction, etc. — all OFF.
The iX1500 requires a button press (touchscreen tap) to start a scan. There's no hands-off auto-trigger. Accept that.
## Vault destinations by category
| Category | Lands in |
|----------|---------|
| `handwritten_notes` | Today's daily note (`#log` section) + optional `Meetings/...md` |
| `meeting_notes` | `Meetings/YYYY-MM-DD - <topic>.md` |
| `receipt` / `invoice` | `Epoch/Finance/receipts/YYYY-MM.md` or `Epoch/Finance/invoices/YYYY-MM.md` |
| `business_card` | `People/<First Last>.md` stub with frontmatter |
| `research_paper` | `Reading/Research/<Title>.md` stub |
| `contract` / `legal` | `Epoch/Legal/inbox.md` + flag |
| `tax_document` | `Epoch/Admin/<year>/tax.md` |
| `kids_school` | `Personal/Family/kids/<year>/` |
| `correspondence` | `Personal/Correspondence/YYYY.md` |
| `other` / unsure | Flag → `~/scansnap-inbox/_needs_review/` |
Every destination note embeds a `file://` link back to the archive PDF, with page range.
## Files
| File | Role |
|------|------|
| `scansnap_bridge.py` | Main daemon (watch, OCR, spawn Claude, file, notify) |
| `prompts/classify.md` | Classification prompt — defines categories, schema, vault conventions |
| `install.sh` | Dep install + plist install |
| `com.idanbeck.scansnap-bridge.plist` | launchd agent (written by `install.sh`) |
| `bridge.log` | Runtime log |
| `.bridge.state.json` | Per-file stability-check state (transient) |
## Failure modes
- **OCR fails** → text extraction falls back to `pdftotext` on the raw scan. Often still works for printed docs.
- **No OCR text** → classifier is given a marker to flag the scan for review.
- **Claude subprocess returns non-JSON** → the bridge logs the output and moves the scan to `_needs_review/`.
- **Filing action references a missing path** → logged as a flag in the Slack notification; vault unchanged.
- **Daemon crashes mid-process** → pending files stay in the inbox and are retried on next start. Source is only moved to archive _after_ successful read; only fully-filed scans are removed from state.
## Customization
- Change the Slack DM target: edit `SLACK_DM_TARGET` in `scansnap_bridge.py`.
- Adjust stability window: `STABILITY_CHECKS` (consecutive polls) and `POLL_INTERVAL_S`.
- Classification timeout: `CLAUDE_TIMEOUT_S`.
- Edit the classification logic by editing `prompts/classify.md` — no code change required.
## Non-goals
- **Never modify the source PDF.** The archive copy is ground truth.
- **No cloud upload.** Everything stays local.
- **No deletion.** Scans are archived, not deleted, even if classification fails.
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.