Problem
Firstmate runs terminal-first — SSH/tmux from a MacBook and from Termux on a phone, over tailscale. Unlike the Claude apps, there's no built-in push when firstmate needs the captain, so the captain has to watch the terminal to catch decisions, review-ready PRs, blockers, and credential prompts. We want push notifications to the captain's laptop and phone when firstmate escalates.
Goal
When firstmate reaches a "must reach the captain" point (see AGENTS.md section 9), fire a push notification to the captain's devices via ntfy (has Android, iOS, web, and desktop clients), so they can respond from wherever they are.
When to notify (the escalation set — section 9; NOT routine progress)
Notify on:
- Work ready for review, with the PR URL.
- Finished investigation findings.
- A gate/decision that needs the captain's call.
- A real blocker or failure after the relevant playbook is exhausted.
- Anything destructive, irreversible, or security-sensitive.
- A needed credential or login.
Explicitly do NOT notify on routine progress, empty polls, automatic fixes/retries, or no-change heartbeats — notification fatigue would kill the signal. This mirrors section 9's "reach immediately" vs "do not surface" split.
Proposed shape (design, not final)
- A small
bin/fm-notify.sh "<title>" "<body>" [--url <link>] helper that publishes to ntfy, fired at escalation points. shellcheck-clean per repo rules.
- Config is LOCAL and gitignored (never committed), following the
config/ + .env convention: ntfy server URL, topic, and any auth token. Presence-gated — absent config means the feature is inert (same pattern as X mode).
- Integration: firstmate (the agent) calls it when it escalates; optionally the supervision watcher / away-mode daemon fires it for wake events that need attention. Compose with the existing away-mode wedge-alarm (
config/wedge-alarm, docs/wedge-alarm.md) rather than duplicating it — ntfy is the cross-device/remote backend that macOS Notification Center can't reach on the phone.
Security / privacy (load-bearing, given the secret-free contract)
ntfy.sh public topics are obscure, not private — anyone who knows the topic name can read it. So decide a posture before shipping:
- (a) self-host ntfy on the box, reachable over tailscale, or
- (b) use an access-token-protected topic, or
- (c) keep notification bodies minimal ("Firstmate needs your input — check the terminal") with no sensitive detail, and keep specifics in the terminal only.
Do not publish PR titles, decision text, or repo details to a public topic. Any token/topic secret stays in local gitignored config, never in the repo.
Acceptance criteria
Problem
Firstmate runs terminal-first — SSH/tmux from a MacBook and from Termux on a phone, over tailscale. Unlike the Claude apps, there's no built-in push when firstmate needs the captain, so the captain has to watch the terminal to catch decisions, review-ready PRs, blockers, and credential prompts. We want push notifications to the captain's laptop and phone when firstmate escalates.
Goal
When firstmate reaches a "must reach the captain" point (see
AGENTS.mdsection 9), fire a push notification to the captain's devices via ntfy (has Android, iOS, web, and desktop clients), so they can respond from wherever they are.When to notify (the escalation set — section 9; NOT routine progress)
Notify on:
Explicitly do NOT notify on routine progress, empty polls, automatic fixes/retries, or no-change heartbeats — notification fatigue would kill the signal. This mirrors section 9's "reach immediately" vs "do not surface" split.
Proposed shape (design, not final)
bin/fm-notify.sh "<title>" "<body>" [--url <link>]helper that publishes to ntfy, fired at escalation points. shellcheck-clean per repo rules.config/+.envconvention: ntfy server URL, topic, and any auth token. Presence-gated — absent config means the feature is inert (same pattern as X mode).config/wedge-alarm,docs/wedge-alarm.md) rather than duplicating it — ntfy is the cross-device/remote backend that macOS Notification Center can't reach on the phone.Security / privacy (load-bearing, given the secret-free contract)
ntfy.sh public topics are obscure, not private — anyone who knows the topic name can read it. So decide a posture before shipping:
Do not publish PR titles, decision text, or repo details to a public topic. Any token/topic secret stays in local gitignored config, never in the repo.
Acceptance criteria
bin/fm-notify.shpublishes a notification (title, body, optional click-through URL) to the configured target; shellcheck-clean; fails closed if config is absent.