macos-watchdog
Design, deploy, and discipline macOS launchd watchdogs — LaunchAgents/LaunchDaemons that detect a recurring problem and auto-remediate it. Use whenever creating or editing a persistent background monitor / daemon / agent on macOS, writing a launchd plist, scheduling a self-healing script, or when a watchdog has become a disturbance itself: re-launching apps the user quit, firing repeated notifications, re-running its full repair ladder every few minutes on an unfixable network, or hammering the system (crash loops, fork storms, runaway restarts). Also use for stop/disable semantics (bootout vs bootstrap vs disable vs unload), adding cooldown / backoff / notification throttling to a self-healer, binding a monitor's lifecycle to its premise state, or auditing existing LaunchAgents. 中文触发:launchd 守护进程、常驻任务、开机自启、后台监控、定时自愈脚本。 Covers KeepAlive/ThrottleInterval/domains/logging, premise self-checks, auto-cooldown, alert layering, batch throttling.
1How It Works & Mechanism
Design philosophy: Bind the watchdog's lifecycle to its premise state—when its reason for existing vanishes (Wi-Fi disconnected, user quit the target app, prerequisite state missing), it must stand down on its own rather than waiting for manual disabling; the core doctrine of this skill is 'never let a watchdog become a new disturbance'.
**a watchdog's lifecycle is bound to its premise state**. When the condition it exists to fix cannot be fixed by it (broken WiFi, user quit the target app, prerequisite state gone), the watchdog must stand down *by itself*
Deployment mechanisms leverage native launchd tooling: using assets/launchagent.template.plist as a foundation, new-launchagent.sh performs idempotent installation (validates label/interval/program → bootout-if-loaded → writes plist → plutil -lint → launchctl bootstrap → verifies via launchctl print).
plutil -lint "$tmp_plist" >/dev/null || { echo "Generated plist failed plutil -lint" >&2; rm -f "$tmp_plist"; exit 1; }
Cooldown logic is enforced at the application layer rather than via ThrottleInterval: when sourced, watchdog-cooldown.sh provides paused_any/record_exhausted/clear_exhausted/cmd_pause/cmd_resume, tracking consecutive exhausted cycles in a state file and backing off quietly across stepped intervals (default 1800/7200/21600 seconds = 30m→2h→6h).
space-separated escalating tiers, seconds. Default "1800 7200 21600"
Stop semantics explicitly surface deprecation traps: on macOS Ventura+, unload can resurrect jobs if the plist remains present and RunAtLoad is set; modern pairs like bootstrap/bootout and disable/enable (persisting stop across logins) must be used instead.
**Never `launchctl unload`**: deprecated, and on Ventura+ the job re-loads via `RunAtLoad` when the plist stays in place — the "disabled" watchdog fires again
Resurrection prevention gate: If remediation involves URL schemes, open -a, or restarting GUI apps, it must be guarded by a process vitality check and include the -g flag (do not steal foreground focus)—otherwise, the watchdog behaves like 'you quit it, but I force it back open'.
Gate every such action: check the target process is alive before invoking its scheme, and pass `-g` so a legitimate action never pops a window. If the user quit the app mid-remediation, abort the ladder
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| cli | launchctl(bootstrap/bootout/kickstart/print/list/disable/enable) |
| cli | plutil(plist lint 校验) |
| cli | sudo(--system 路径:/Library/LaunchDaemons + system 域 bootstrap) |
| cli | open / log show / id -u(GUI 域引导、launchd 日志排查) |
4Recommended Governance & Evidence
- scripts/: Local script or command execution: scripts/new-launchagent.sh (installation phase: bootout/write plist/bootstrap)
- 本地工作目录: File I/O and artifact output: ~/Library/LaunchAgents/<label>.plist (or sudo /Library/LaunchDaemons/<label>.
5Second-Pass Verification
- [ok] 四条款契约与真实事故绑定 — SKILL.md 每条 clause 后都带真实案例('Real case: a recovery watcher kept firing for 2h after its premise resolved'、'unthrottled test replay forked 1,041 processes/sec for 7 minutes' 等);war stories 在 references/quiet-watchdog-patterns.md。
- [ok] new-launchagent.sh 校验与安装流程 — label reverse-DNS 校验、interval 整数且 ≥30、program -f/-x 绝对路径、tmp plist + plutil -lint、bootout-if-loaded、launchctl bootstrap + print 验证,全部逐字在源码。
- [ok] cool-down 状态机(1800/7200/21600 阶梯) — watchdog-cooldown.sh 常量与文档默认一致;state 文件 pause/autopause/exhausted.state;提供 cmd_pause/cmd_resume/record_exhausted/clear_exhausted/paused_any。
- [ok] unload 陷阱与 bootstrap/bootout 语义 — SKILL.md 'Never `launchctl unload`' + Ventura+ RunAtLoad 复活机制说明 + bootout/disable 命令表,与脚本实现一致。
- [ok] 运行路径无网络(安全结论基础) — 全目录运行代码无 curl/wget/http 调用;唯一 curl 出现在 evals/iteration-1 fixture broken-heal.sh(评测反例夹具),已在 internal_assets/风险注记中说明非运行路径。
- [ok] 无凭证读取 + plist 密钥警告 — plist 模板 'Never put secrets here; the plist is world-readable.';脚本无 token/API key 处理。
- [ok] 元数据(commit/license/stars) — 本地 HEAD == pin d5c4678cb5d4fd6acc9c922690df035dbd33d247;GitHub API:MIT、stars 1385(2026-09-09 实采)、pushed_at 2026-09-09T12:33:29Z。
6Verdict & Scenarios
84c303ee98ee57c6…