From 0825c74b693efa4c6c260ff882275b6e79c9cf89 Mon Sep 17 00:00:00 2001 From: marcusquinn <6428977+marcusquinn@users.noreply.github.com> Date: Thu, 5 Mar 2026 23:14:14 +0000 Subject: [PATCH 1/2] fix: add process guard to setup.sh and enable pulse RunAtLoad for reboot survival Two changes to prevent kernel panics from runaway processes: 1. Pulse plist: RunAtLoad=true so the supervisor pulse restarts after reboot/panic. Previously RunAtLoad=false meant the pulse never restarted, leaving the system unmonitored after crashes. 2. Process guard: Added to setup.sh so all users get it on install/update. Previously the guard plist was manually created and not managed by setup. Now installs automatically: - macOS: launchd plist, 30s interval (was 120s), RunAtLoad=true - Linux: cron entry, every minute (cron minimum granularity) - No consent needed (safety net, not autonomous action) - Kills ShellCheck processes >512MB RSS or >120s runtime - Kills other AI processes >8GB RSS or >2h runtime Context: 5 kernel panics in one day from watchdog timeouts caused by ShellCheck memory bloat (2GB+ per instance) exhausting the compressor segment limit. The shellcheck-wrapper.sh (PR #2918) fixes the root cause; this ensures the guard catches any remaining edge cases. --- setup.sh | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index 87ad5514a1..46790bafd6 100755 --- a/setup.sh +++ b/setup.sh @@ -900,7 +900,7 @@ main() { 1800 RunAtLoad - + KeepAlive @@ -977,6 +977,83 @@ PLIST fi fi + # Process guard — kills runaway AI processes (ShellCheck bloat, stuck workers) + # before they exhaust memory and cause kernel panics. Always installed when the + # script exists; no consent needed (safety net, not autonomous action). + # macOS: launchd plist (30s interval, RunAtLoad=true) | Linux: cron (every minute) + local guard_script="$HOME/.aidevops/agents/scripts/process-guard-helper.sh" + local guard_label="sh.aidevops.process-guard" + if [[ -x "$guard_script" ]]; then + mkdir -p "$HOME/.aidevops/logs" + + if [[ "$(uname -s)" == "Darwin" ]]; then + local guard_plist="$HOME/Library/LaunchAgents/${guard_label}.plist" + + # Unload old plist if upgrading + if _launchd_has_agent "$guard_label"; then + launchctl unload "$guard_plist" 2>/dev/null || true + fi + + cat >"$guard_plist" < + + + + Label + ${guard_label} + ProgramArguments + + ${guard_script} + kill-runaways + + StartInterval + 30 + StandardOutPath + ${HOME}/.aidevops/logs/process-guard.log + StandardErrorPath + ${HOME}/.aidevops/logs/process-guard.log + EnvironmentVariables + + PATH + /opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + SHELLCHECK_RSS_LIMIT_KB + 524288 + SHELLCHECK_RUNTIME_LIMIT + 120 + CHILD_RSS_LIMIT_KB + 8388608 + CHILD_RUNTIME_LIMIT + 7200 + + RunAtLoad + + KeepAlive + + + +GUARD_PLIST + + if launchctl load "$guard_plist" 2>/dev/null; then + print_info "Process guard enabled (launchd, every 30s, survives reboot)" + else + print_warning "Failed to load process guard LaunchAgent" + fi + else + # Linux: cron entry (every minute — cron minimum granularity) + if ! crontab -l 2>/dev/null | grep -qF "aidevops: process-guard" 2>/dev/null; then + ( + crontab -l 2>/dev/null | grep -v 'aidevops: process-guard' + echo "* * * * * SHELLCHECK_RSS_LIMIT_KB=524288 SHELLCHECK_RUNTIME_LIMIT=120 CHILD_RSS_LIMIT_KB=8388608 CHILD_RUNTIME_LIMIT=7200 /bin/bash ${guard_script} kill-runaways >> \$HOME/.aidevops/logs/process-guard.log 2>&1 # aidevops: process-guard" + ) | crontab - 2>/dev/null || true + fi + if crontab -l 2>/dev/null | grep -qF "aidevops: process-guard" 2>/dev/null; then + print_info "Process guard enabled (cron, every minute)" + else + print_warning "Failed to install process guard cron entry" + fi + fi + fi + echo "" echo "CLI Command:" echo " aidevops init - Initialize aidevops in a project" From 97cbf06d5cf7f741266f39da13ca45af7dfe8cd9 Mon Sep 17 00:00:00 2001 From: marcusquinn <6428977+marcusquinn@users.noreply.github.com> Date: Thu, 5 Mar 2026 23:56:10 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20address=20review=20feedback=20?= =?UTF-8?q?=E2=80=94=20dynamic=20PATH,=20HOME=20env,=20quoted=20cron=20pat?= =?UTF-8?q?hs,=20remove=20stderr=20suppression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use dynamic ${PATH} and add ${HOME} to guard plist env vars for consistency with the pulse plist pattern (CodeRabbit, Gemini) - Remove 2>/dev/null from launchctl unload/load — || true handles errors, stderr aids debugging (Gemini) - Quote paths in cron entry to handle spaces in paths (Gemini) - Always regenerate cron entry to pick up config changes, matching macOS plist behavior (CodeRabbit) --- setup.sh | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/setup.sh b/setup.sh index 46790bafd6..3d3f0b7eb1 100755 --- a/setup.sh +++ b/setup.sh @@ -991,7 +991,7 @@ PLIST # Unload old plist if upgrading if _launchd_has_agent "$guard_label"; then - launchctl unload "$guard_plist" 2>/dev/null || true + launchctl unload "$guard_plist" || true fi cat >"$guard_plist" <EnvironmentVariables PATH - /opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + ${PATH} + HOME + ${HOME} SHELLCHECK_RSS_LIMIT_KB 524288 SHELLCHECK_RUNTIME_LIMIT @@ -1033,19 +1035,18 @@ PLIST GUARD_PLIST - if launchctl load "$guard_plist" 2>/dev/null; then + if launchctl load "$guard_plist"; then print_info "Process guard enabled (launchd, every 30s, survives reboot)" else print_warning "Failed to load process guard LaunchAgent" fi else # Linux: cron entry (every minute — cron minimum granularity) - if ! crontab -l 2>/dev/null | grep -qF "aidevops: process-guard" 2>/dev/null; then - ( - crontab -l 2>/dev/null | grep -v 'aidevops: process-guard' - echo "* * * * * SHELLCHECK_RSS_LIMIT_KB=524288 SHELLCHECK_RUNTIME_LIMIT=120 CHILD_RSS_LIMIT_KB=8388608 CHILD_RUNTIME_LIMIT=7200 /bin/bash ${guard_script} kill-runaways >> \$HOME/.aidevops/logs/process-guard.log 2>&1 # aidevops: process-guard" - ) | crontab - 2>/dev/null || true - fi + # Always regenerate to pick up config changes (matches macOS behavior) + ( + crontab -l 2>/dev/null | grep -v 'aidevops: process-guard' + echo "* * * * * SHELLCHECK_RSS_LIMIT_KB=524288 SHELLCHECK_RUNTIME_LIMIT=120 CHILD_RSS_LIMIT_KB=8388608 CHILD_RUNTIME_LIMIT=7200 /bin/bash \"${guard_script}\" kill-runaways >> \"\$HOME/.aidevops/logs/process-guard.log\" 2>&1 # aidevops: process-guard" + ) | crontab - 2>/dev/null || true if crontab -l 2>/dev/null | grep -qF "aidevops: process-guard" 2>/dev/null; then print_info "Process guard enabled (cron, every minute)" else