Skip to the content.

Trigger → worker chassis

← Back to home

Four pipelines in this repo share the same shape. Once you’ve seen one, you’ve seen all four — and the fifth one you build will follow it too.

The chassis: (filesystem-or-OS event) → debounce + lock → worker dispatch → result handler.

The four instances

1. Finder tag → action

You tag a file with process in Finder. bin/tag-watcher (FSEvents + xattr poll fallback) notices, locks, and hands the path to bin/tag-result-handler which routes based on which tag fired (process, needs-ocr, etc.). Full pipeline doc: wiki/concepts/finder-tag-pipeline.md.

The companion /tag-app command generates one Finder-toolbar .app per tag name so the tag itself is one click away.

2. Voice Memo #process → whisp

You record a Voice Memo with #process in the title. A watcher polls Voice Memos.app’s database, extracts the .m4a + the tsrp auto-transcript atom (see the gotcha), hands the audio to the whisp pipeline (OpenAI Whisper local, with hallucination-loop stripping). Result: a richer transcript than the OS-provided one, summarized, filed, and a notification fires when done.

3. Stickies → Claude

You edit a Sticky note. FSEvents inside the AppleToolbox process notices, debounces, and dispatches a worker that reads the note as the next prompt for Claude. The note is the trigger surface. Doc: wiki/concepts/stickies-claude-trigger.md.

4. Mail flag → routing

You flag a Mail message with one of seven colors. A poll (Mail Rules cannot trigger on user-flagging — only on incoming-mail attributes) notices the flag, walks the per-color contract in bin/mail-flag-config.json, and fans out: writes the .eml, extracts attachments, renders the body as markdown, files them in per-color destinations. Doc: wiki/concepts/mail-flag-pipeline.md.


What makes the chassis reusable

Every instance has the same five rules. Violating any of them re-introduces a bug we’ve already fixed.

1. Lock before dispatch

FSEvents fires N times per save (one .rtfd write triggers ~3–5 events on Sequoia). Without mkdir-style lock, two worker processes race and dispatch twice. (2026-05-25 incident: Stickies opened two iTerm windows per save. Fix in 30eef70.)

2. Belt + suspenders triggers

FSEvents is fast but unreliable across sandboxes; the periodic Timer poll is slow but exhaustive. Run both. Lock dedupes them.

3. Run inside AppleToolbox, not a bare LaunchAgent

TCC silently filters directory listings for LaunchAgent-spawned bash inside app containers. The watcher needs to live inside an app with Full Disk Access — that’s AppleToolbox. Full debugging story: feedback_launchd_cannot_read_app_containers.md (in private project memory, summarized in wiki/entities/appletoolbox.md).

4. Calibrate the sensor

If the trigger is a measurement (HomePod climate sensor, Voice Memos transcript confidence, tag colour), measure the bias once against a trusted reference and apply the offset before downstream code consumes the value. HomePod reads 0.45 °C and 4.5 % low against a professional olosuhdemittaus instrument; we add those offsets.

5. Result handler is separate from the watcher

tag-watcher notices, tag-result-handler decides what to do. Mail-flag dispatch is in bin/mail-flag-worker, separate from the poller. Keeping the noticing and the doing apart lets you swap one without touching the other.


Adding a fifth

When the next “X happens → script runs” idea arrives, the chassis already exists. New watcher = new entry in AppleToolbox’s Timer callback list + an FSEvents subscription, dispatching to a new worker in bin/. The four pipelines above give the precedent for every nuance.

Skill memory: Mail flag pipeline as the fourth instance of the chassis.


← Back to home Triggers ← Tiers ← Sal corpus ←