Summary
flow do calls osascript to spawn an iTerm tab. macOS TCC keys Apple Events permission on the calling binary's code-signing identifier. Because flow ships unsigned and macOS applies ad-hoc signing with the default identifier scheme — flow-<sha1-of-binary> — every release produces a new identifier, so any previously-granted Automation permission becomes invalid on upgrade.
Result: flow do silently fails with Not authorised to send Apple events to iTerm. (-1743) after each upgrade until the user re-grants Automation. Because TCC doesn't always show a prompt for headless callers, the only recovery is manual intervention in System Settings → Privacy & Security → Automation.
Reproduction
- On macOS, install any version of flow.
- Run
flow do <slug>; grant Automation when macOS prompts. Tab spawns.
- Upgrade flow to a new release (any version bump that changes the binary).
- Run
flow do <slug> again.
Expected: tab spawns. Actual: error: osascript failed: ... Not authorised to send Apple events to iTerm. (-1743).
Root cause
$ codesign -dvv $(which flow)
Identifier=flow-555549445d3eb9e0bbce3434fd1ba6512765a843 ← SHA-1 of binary
Signature=adhoc
The trailing hash changes every build. macOS sees alpha.18's flow-555549... as a different app from alpha.15's flow-<otherHash>, so the old TCC grant doesn't apply. TCC also doesn't reliably prompt for binaries without an Info.plist / bundle ID — it sometimes records a silent denial.
Confirmed by tracing flow's osascript invocation through a PATH shim: the AppleScript is identical to a manual osascript call from any shell, but the same script run via flow's exec fails while the manual call succeeds, because Claude.app (the parent terminal here) HAS the iTerm Automation grant, but flow-<hash> does not.
Fix
Pin a stable codesign identifier at build time:
codesign --force --identifier cloud.facets.flow --sign - dist/flow
(Or the equivalent in goreleaser / your release pipeline.) Once flow ships with a fixed identifier, the user grants Automation once and upgrades preserve it.
Workaround for affected users today
codesign --force --identifier cloud.facets.flow --sign - $(which flow)
…then re-grant Automation → iTerm in System Settings. Works, but the user must re-run after every flow skill update / binary refresh.
Environment
- flow
v0.1.0-alpha.18
- macOS (Apple Silicon, ad-hoc signature, no Info.plist)
- Calling context: Claude Code session forking flow via Bash
Why this matters
For users like me running the pr-watch playbook, an upgrade silently breaks the entire PR-review pipeline — tasks land in backlog, the monitor marks them error:rc=2 in pr-watch-state.db, and nothing recovers until manual intervention. Stable codesign identity removes the entire failure mode.
Summary
flow docallsosascriptto spawn an iTerm tab. macOS TCC keys Apple Events permission on the calling binary's code-signing identifier. Because flow ships unsigned and macOS applies ad-hoc signing with the default identifier scheme —flow-<sha1-of-binary>— every release produces a new identifier, so any previously-granted Automation permission becomes invalid on upgrade.Result:
flow dosilently fails withNot authorised to send Apple events to iTerm. (-1743)after each upgrade until the user re-grants Automation. Because TCC doesn't always show a prompt for headless callers, the only recovery is manual intervention in System Settings → Privacy & Security → Automation.Reproduction
flow do <slug>; grant Automation when macOS prompts. Tab spawns.flow do <slug>again.Expected: tab spawns. Actual:
error: osascript failed: ... Not authorised to send Apple events to iTerm. (-1743).Root cause
The trailing hash changes every build. macOS sees alpha.18's
flow-555549...as a different app from alpha.15'sflow-<otherHash>, so the old TCC grant doesn't apply. TCC also doesn't reliably prompt for binaries without anInfo.plist/ bundle ID — it sometimes records a silent denial.Confirmed by tracing flow's osascript invocation through a PATH shim: the AppleScript is identical to a manual osascript call from any shell, but the same script run via flow's exec fails while the manual call succeeds, because Claude.app (the parent terminal here) HAS the iTerm Automation grant, but
flow-<hash>does not.Fix
Pin a stable codesign identifier at build time:
(Or the equivalent in goreleaser / your release pipeline.) Once flow ships with a fixed identifier, the user grants Automation once and upgrades preserve it.
Workaround for affected users today
codesign --force --identifier cloud.facets.flow --sign - $(which flow)…then re-grant Automation → iTerm in System Settings. Works, but the user must re-run after every
flow skill update/ binary refresh.Environment
v0.1.0-alpha.18Why this matters
For users like me running the
pr-watchplaybook, an upgrade silently breaks the entire PR-review pipeline — tasks land in backlog, the monitor marks themerror:rc=2inpr-watch-state.db, and nothing recovers until manual intervention. Stable codesign identity removes the entire failure mode.