결론
5개 plist의 OpenClaw → Hermes 경로 rewrite와 스크립트 직접 실행 검증은 완료했다.
하지만 이 세션의 현재 실행 권한/샌드박스에서는 launchctl bootstrap이 모두 Bootstrap failed: 5: Input/output error로 막혀, launchd 재등록은 완료하지 못했다.
중요: bootout은 실행되어 현재 5개 label 모두 launchd domain에서 빠진 상태다. 즉 plist 파일은 고쳐졌지만 launchd에는 아직 재등록되지 않았다. 외부 터미널 또는 권한 있는 셸에서 bootstrap만 다시 실행해야 한다.
수동 재등록 명령:
uid=$(id -u)
launchctl bootstrap gui/$uid ~/Library/LaunchAgents/com.openclaw.cowork-daily-review.plist
launchctl bootstrap gui/$uid ~/Library/LaunchAgents/com.openclaw.autopilot-sweeper.plist
launchctl bootstrap gui/$uid ~/Library/LaunchAgents/com.openclaw.inbox-triage.plist
launchctl bootstrap gui/$uid ~/Library/LaunchAgents/ai.openclaw.memory-guardian.plist
launchctl bootstrap gui/$uid ~/Library/LaunchAgents/com.openclaw.ron-orchestrator.plist
백업/수정 위치
백업:
/Users/ron/.hermes/backups/launchagents_ops_revival_20260415_160915/
수정한 plist:
~/Library/LaunchAgents/com.openclaw.cowork-daily-review.plist
~/Library/LaunchAgents/com.openclaw.autopilot-sweeper.plist
~/Library/LaunchAgents/com.openclaw.inbox-triage.plist
~/Library/LaunchAgents/ai.openclaw.memory-guardian.plist
~/Library/LaunchAgents/com.openclaw.ron-orchestrator.plist
수정한 스크립트:
~/.hermes/workspace/scripts/ron_orchestrator_controller.py
결과 표
| label | 재기동 | last exit | cwd | 의미 | 관련 메시지 |
|---|---|---|---|---|---|
com.openclaw.cowork-daily-review |
❌ bootstrap 차단 | 기존 0 / 직접 skip test 0 | /Users/ron/.hermes/workspace/scripts |
Cowork 세션 요약/정기 점검. FULL/LITE/TRIAGE 모드, Claude CLI 실행, 필요 시 Telegram 보고. | 기존 stderr의 bad substitution은 Hermes script에서 tr 방식으로 이미 해결되어 있음. |
com.openclaw.autopilot-sweeper |
❌ bootstrap 차단 | 기존 0 / 직접 --apply 0 |
/Users/ron/.hermes/workspace |
Hermes cron jobs.json에서 최근 재시도+연속오류 job을 안전 비활성화. | 기존 OpenClaw 로그의 <REPLACED_BY_PATCH>/No module named scripts 문제는 Hermes script 기준 없음. |
com.openclaw.inbox-triage |
❌ bootstrap 차단 | 기존 0 / bash -n 0 |
/Users/ron/.hermes/workspace/scripts |
COWORK_MODE_OVERRIDE=TRIAGE로 볼트/수신함 분류. |
직접 full run은 Claude/Telegram 장시간 작업이라 구문 검증만 수행. |
ai.openclaw.memory-guardian |
❌ bootstrap 차단 | 기존 0 / 직접 실행 0 | /Users/ron/.hermes/workspace |
매분 vm_stat/swap/compressor/top process 체크, leak 감지, 단계별 정리, Telegram critical 경고. | 직접 실행 로그: ok available=5252MB compressor=3.9GB seg=46% swap=0MB. |
com.openclaw.ron-orchestrator |
❌ bootstrap 차단 | 기존 1 → 직접 실행 0 | /Users/ron/.hermes/workspace |
bus command 상태 집계, stuck claimed 작업 복구, cowork 결과 처리. | exit 1 원인은 Python 3.9 타입힌트 평가 + jobs state 타입 가정 버그. 수정 완료. |
plist rewrite 상세
cowork-daily-review
변경:
ProgramArguments:
/bin/bash
/Users/ron/.hermes/workspace/scripts/cowork_daily_review.sh
WorkingDirectory: /Users/ron/.hermes/workspace/scripts
stdout: /Users/ron/.hermes/logs/cowork_daily_review_stdout.log
stderr: /Users/ron/.hermes/logs/cowork_daily_review_stderr.log
검증:
bash -n ~/.hermes/workspace/scripts/cowork_daily_review.sh => OK
COWORK_TEST_HOUR=10 .../cowork_daily_review.sh => exit 0
~/.hermes/logs/cowork_daily_review.log:
[2026-04-15 16:09:23] === Cowork LITE skipped (reduced frequency) ===
의미: Cowork 일일/주기 리뷰. 시간대에 따라 FULL/LITE, inbox-triage에서는 TRIAGE 모드로 동작. Telegram DM 보고 로직 포함.
autopilot-sweeper
변경:
ProgramArguments:
/usr/bin/python3
/Users/ron/.hermes/workspace/scripts/autopilot_sweeper.py
--apply
WorkingDirectory: /Users/ron/.hermes/workspace
stdout: /Users/ron/.hermes/logs/autopilot_sweeper.stdout.log
stderr: /Users/ron/.hermes/logs/autopilot_sweeper.stderr.log
검증:
/usr/bin/python3 ~/.hermes/workspace/scripts/autopilot_sweeper.py --check
proposed_disable_count= 0
ids= []
/usr/bin/python3 ~/.hermes/workspace/scripts/autopilot_sweeper.py --apply
applied: {'backup': '/Users/ron/.hermes/cron/backups/jobs.json.bak.20260415T070923Z', 'changed': 0, 'per_job_errors': []}
의미: 크론 실패 패턴을 보고 안전 조건(consecutiveErrors>=3, 최근 재시도 존재)에 맞으면 Hermes cron job을 비활성화한다. 이번 검증에서는 변경 0건.
inbox-triage
변경:
ProgramArguments:
/bin/bash
/Users/ron/.hermes/workspace/scripts/cowork_daily_review.sh
EnvironmentVariables:
COWORK_MODE_OVERRIDE=TRIAGE
WorkingDirectory: /Users/ron/.hermes/workspace/scripts
stdout: /Users/ron/.hermes/logs/inbox_triage_stdout.log
stderr: /Users/ron/.hermes/logs/inbox_triage_stderr.log
검증:
COWORK_MODE_OVERRIDE=TRIAGE bash -n ~/.hermes/workspace/scripts/cowork_daily_review.sh => OK
의미: 같은 Cowork script를 TRIAGE 모드로 실행해 수신함 분류를 수행한다. 대상 prompt는 ~/.hermes/workspace/scripts/inbox_triage_prompt.md.
memory-guardian
변경:
ProgramArguments:
/usr/bin/python3
/Users/ron/.hermes/workspace/scripts/monitoring/memory_guardian.py
WorkingDirectory: /Users/ron/.hermes/workspace
stdout: /Users/ron/.hermes/logs/memory_guardian.out
stderr: /Users/ron/.hermes/logs/memory_guardian.err
검증:
/usr/bin/python3 ~/.hermes/workspace/scripts/monitoring/memory_guardian.py => exit 0
Hermes 로그:
2026-04-15 16:09:23 seg_pct sysctl rc=1 stderr=sysctl: sysctl fmt -1 1024 1: Operation not permitted
2026-04-15 16:09:23 seg_pct fallback vm_stat ratio=45.9% (occupied=256233 stored=558636)
2026-04-15 16:09:23 hermes gateway not running, skip watchdog
2026-04-15 16:09:23 ok available=5252MB compressor=3.9GB seg=46% swap=0MB
의미: 메모리 압박 감시. vm_stat, swap, compressor segment, top process RSS를 기록하고, 임계 시 Telegram critical 경고를 보낸다. 현재 sandbox에서는 sysctl 일부가 막혀 fallback으로 동작했다.
ron-orchestrator
변경:
ProgramArguments:
/usr/bin/python3
/Users/ron/.hermes/workspace/scripts/ron_orchestrator_controller.py
WorkingDirectory: /Users/ron/.hermes/workspace
stdout: /Users/ron/.hermes/logs/ron_orchestrator.log
stderr: /Users/ron/.hermes/logs/ron_orchestrator_error.log
원래 exit 1 원인:
TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'
상세 원인: LaunchAgent가 Python 3.9로 실행하는데, script가 float | None 타입힌트를 런타임 평가했다. Python 3.9에서는 from __future__ import annotations 없이는 이 표현이 런타임 TypeError를 낸다.
추가 발견/수정:
- 첫 수정 후 직접 실행에서 jobs.json의
state가 dict가 아니라 string인 job 때문에 추가 exit 1 발생. check_cowork_results()에서 job이 dict인지,state가 dict인지 방어하도록 수정.
수정 내용:
1. from __future__ import annotations 추가
2. check_cowork_results()에서 non-dict job/state 방어 및 consecutiveErrors int 변환
검증:
/usr/bin/python3 ~/.hermes/workspace/scripts/ron_orchestrator_controller.py => exit 0
python3 -m py_compile ~/.hermes/workspace/scripts/ron_orchestrator_controller.py => OK
직접 실행 중 실제 조치 1건:
✅ Task 29183 (cowork): codex 패치 배포용 최종 체크리스트 및 롤백 정책 ...
즉, stuck claimed 작업 1건을 자동 완료 처리했다.
launchctl 재등록 실패 내역
실행:
launchctl bootout gui/501/<label>
launchctl bootstrap gui/501 <plist>
결과:
Bootstrap failed: 5: Input/output error
Try re-running the command as root for richer errors.
Bad request.
Could not find service "<label>" in domain for user gui: 501
간단한 테스트용 LaunchAgent도 같은 Bootstrap failed: 5가 발생했다. 따라서 개별 plist 내용 문제가 아니라 현재 세션에서 launchd bootstrap 권한/샌드박스가 막힌 것으로 판단한다.
현재 상태:
launchctl print gui/501/com.openclaw.cowork-daily-review => Could not find service
launchctl print gui/501/com.openclaw.autopilot-sweeper => Could not find service
launchctl print gui/501/com.openclaw.inbox-triage => Could not find service
launchctl print gui/501/ai.openclaw.memory-guardian => Could not find service
launchctl print gui/501/com.openclaw.ron-orchestrator => Could not find service
다음 조치
권한 있는 일반 터미널에서 아래만 실행하면 된다. plist와 스크립트는 이미 수정되어 있다.
uid=$(id -u)
for plist in \
~/Library/LaunchAgents/com.openclaw.cowork-daily-review.plist \
~/Library/LaunchAgents/com.openclaw.autopilot-sweeper.plist \
~/Library/LaunchAgents/com.openclaw.inbox-triage.plist \
~/Library/LaunchAgents/ai.openclaw.memory-guardian.plist \
~/Library/LaunchAgents/com.openclaw.ron-orchestrator.plist; do
launchctl bootstrap gui/$uid "$plist"
done
launchctl list | grep -E 'cowork-daily-review|autopilot-sweeper|inbox-triage|memory-guardian|ron-orchestrator'
재등록 후 즉시 확인할 것:
launchctl print gui/$(id -u)/com.openclaw.ron-orchestrator | grep -E 'state|last exit code|program|working directory'
launchctl print gui/$(id -u)/ai.openclaw.memory-guardian | grep -E 'state|last exit code|program|working directory'
자체평가
- 정확성: 4.2/5 — plist rewrite와 스크립트 원인은 확인/수정했다. launchd bootstrap은 세션 권한 문제로 미완.
- 완성도: 3.8/5 — 데몬 재등록이 실제 완료되지 않아 완전 복구는 아니다. 단, 재등록 직전 상태까지는 준비 완료.
- 검증: 4.0/5 — 직접 실행 exit 0, plist lint, 경로 확인 완료. launchctl bootstrap은 실패 증거 확보.
- 최소 변경: 4.6/5 — 대상 plist 5개와 ron orchestrator 방어 수정만 수행.
종합: 4.15/5. launchctl bootstrap을 권한 있는 터미널에서 재실행해야 최종 완료다.