Lab notes
Each app page ends with a paragraph on the hardest part of building it. These are the long versions: the symptom, the cause, the fix that held and how to check it, written from each app’s source and checked against the platform’s own documentation.
Anthropic API key in an iOS app binary: move it to the Keychain
A Swift string literal ships in the app binary, readable with strings. Store a user-supplied key in the Keychain with a ThisDeviceOnly class instead.
AudioContext was not allowed to start: make the click the UI
Chrome starts an AudioContext made before any user gesture suspended. Create or resume() it in a click or keydown handler, like Meridian 7's power button.
AXIsProcessTrusted false after rebuild: ad-hoc signing and TCC
An ad-hoc signature's designated requirement is the build's cdhash, so changed code no longer matches its Accessibility grant. Sign with a stable identity.
CGWindowListCreateImage unavailable in macOS 15: use SCStream
CGWindowListCreateImage is deprecated in macOS 14 and a compile error from a macOS 15 target. Anchor still uses it; here is the SCStream replacement.
Check a password against Have I Been Pwned without sending it
SHA-1 the password, send only the first 5 hex characters to the Pwned Passwords range API, match the suffix locally, and add Add-Padding: true.
Claude Code signed out by another app: refresh token rotation
Anthropic rotates the refresh token on every refresh, so another app that refreshes Claude Code's login signs it out. Only Claude Code should refresh it.
CMIO camera extension: personal teams can't sign System Extension
A Core Media I/O camera is a system extension. Apple doesn't offer the System Extension capability to free accounts, so a personal team can't sign it.
Hide a macOS app the moment it activates, then ask for Touch ID
Hide the app inside the didActivateApplicationNotification handler, then run LAContext. It can't promise no flash: the app is already active.
Intercept Cmd+Q with a CGEventTap and repost it without a loop
Drop Cmd+Q in an active session tap, then send a new key-down/up with CGEventPostToPid. It enters after session taps, so your own tap never sees it.
Karplus-Strong with tanh saturation never decays: normalise by drive
tanh(drive·x)/tanh(drive) has slope drive/tanh(drive) > 1 at zero, so a Karplus-Strong loop using it self-oscillates. Divide by drive instead.
Laplacian sharpness always 0 on iOS: Core Image and Vision traps
Rendering a Laplacian to .RGBA8 with a grey colour space wrote nothing, and caught Vision errors became 0. Render to .Rf and return nil when Vision fails.
Mac Dock animation speed: autohide-time-modifier and killall Dock
defaults write com.apple.dock autohide-time-modifier sets the Dock's auto-hide speed; killall Dock applies it. On a schedule, restart only on real changes.
macOS global hotkey: RegisterEventHotKey vs an NSEvent monitor
A Carbon hotkey that never fires may just be registered late. An NSEvent monitor needs Accessibility and can't stop the key reaching the frontmost app.
Neon never scales to zero under a one-minute cron job
Neon suspends a compute only after 5 idle minutes, so a query every minute keeps it awake all month. We moved live state to Vercel Blob with gated writes.
NSWindow behind all app windows on macOS: use normal level minus one
A window at the desktop-icon level + 1 painted over a maximized Chrome window in testing. NSWindow.Level.normal.rawValue - 1 kept it behind app windows.
Per-app power on Apple silicon: Mach ticks and ri_energy_nj
proc_pidinfo CPU times on Apple silicon are Mach ticks: scale by 125/3, not 1. For per-process energy, proc_pid_rusage has ri_energy_nj from macOS 13.
StoreKit 2 refunded purchase still unlocked: use currentEntitlements
Transaction.all still returns refunded purchases. Check Transaction.currentEntitlements instead, or in expo-iap pass onlyIncludeActiveItemsIOS: true.
Swift menu bar app stops updating: Timer run loop mode and App Nap
A Timer in the default run loop mode doesn't fire while a menu is open, and App Nap throttles it later. Add it in .common and hold a beginActivity token.
THREE.Sprite ignores negative scale: how to flip a sprite in three.js
scale.x = -1 can't flip a THREE.Sprite: its shader uses the length of each matrix column. Flip the UVs, or draw on a camera-facing PlaneGeometry.
Why an approval queue for AI agent edits fails, and what works
Agents write with their own file tools, so a propose-edit MCP tool gets bypassed. Watch the folder, rank changes, and pin sign-offs to a content hash.