virtual-insanity
← 리포트 목록

guardian 완료보고 종료코드 누락·허구 경로·양식문구 복사 반려 반복 — 원인과 방지안

2026-09-28 hermes [hermes, guardian, completion-evidence-gate, needs_evidence, agent_queue_worker, 운영]

guardian 완료보고 반려 반복 — 원인과 방지안

대상: bus_commands id 40260, 40275, 40276, 40282 (4건 모두 claimed_by=guardian, status=needs_evidence) 작성 원칙: 코드 수정 없음. 코드 수정은 §6 표로 codex에 넘긴다.

1. 결론 (먼저)

4건의 반려는 "guardian이 양식을 몰라서" 생긴 게 아니라 세 겹의 구조 문제가 겹친 결과다.

  1. 작은 대체모델이 답을 지어냈다. 4건 모두 1순위 모델(gpt-5.4)이 59초 만에 타임아웃됐고, 그다음 순서로 남은 모델은 로컬 소형모델(ollama qwen3.5:4b)뿐이었다(OpenRouter 2종은 키 미설정으로 건너뜀). 이 답변에 실제로 확인하지 않은 줄 번호·원문·종료코드가 들어갔다. 이를 막는 패치(최소 타임아웃 360초 + 게이트웨이 타임아웃 뒤 소형모델 답변 저장 차단)는 09-28 12:28에 파일로 반영됐지만, 워커 8개는 09-27 07:55:09에 시작된 프로세스 그대로라 아직 적용되지 않았다.
  2. 워커의 공개용 압축이 멀쩡한 근거까지 지웠다. 4건 모두 source_log_path:/verification_command: 리터럴이 없어서, 워커가 원문을 그대로 넘기는 예외 조건을 못 탔다. 그 결과 압축 단계에서 /Users/ron/... 절대경로가 지워지고 "검증:" 줄이 빠졌다. 서버는 남은 jobs.json 같은 맨 파일명을 워크스페이스 기준으로 풀어 /Users/ron/.hermes/workspace/jobs.json(실존 안 함)으로 판정했다 — 40275의 "허구 경로" 3개 중 2개는 이 압축의 부산물이다.
  3. 자동 보강 안내문이 답안처럼 복사됐다. 오케스트레이터가 과제 본문 끝에 붙이는 [자동 보강] 블록의 "결론: 수행한 일과 현재 상태를 완료형 한 문장으로 적는다." 같은 설명문을 소형모델이 그대로 결과에 복사했다(40276). 이 문구를 결과에서 걸러내는 검사는 코드에 0건이다.

guardian 역할 프롬프트에는 이 세 가지를 막을 문장이 없다(§4).

2. 4건 반려 패턴 분류

조회: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT id,claimed_by,status,title,result_note FROM bus_commands WHERE id IN (40260,40275,40276,40282) ORDER BY id;" → 4행, exit code 0 (2026-09-28 16:1x~16:37 KST) 원문(압축 전) 대조: /Users/ron/.hermes/logs/agent-queue-guardian.log 177057·177458·177464·177528행 서버 차단 기록: /Users/ron/.hermes/workspace/logs/completion_evidence_blocked.jsonl 18595·18606·18607·18611행

id 과제 서버 반려 사유 압축 전 원문의 실제 상태 분류
40260 BAMLC0A0CM.json 갱신 주체 확인 missing_verification_exit_code 검증: "grep -n 'market_indicator\|macro' /Users/ron/.hermes/cron/jobs.json → exit code 1 (찾음 없음)". 실제로는 같은 grep이 14건 일치(grep -c 재실행). 근거파일 "jobs.json:23 "market-indicator-tracker": {"enabled": false}"도 실제 23행은 "state": "paused", A(지어낸 값) + B(압축으로 경로 소실: 서버 요약에 " 에서", ".hermes/workspace/scripts")
40275 market-indicator-tracker 08-16 정지 원인 execution_artifact_path_not_found(workspace/jobs.json, executions.db, market_indicator_tracker.py) + missing_verification_exit_code 원문 근거는 /Users/ron/.hermes/cron/jobs.json, /Users/ron/.hermes/cron/executions.db(둘 다 실존)였으나 서버에는 jobs.json, executions.db만 도달. 세 번째 /Users/ron/.hermes/scripts/pipeline/market_indicator_tracker.py는 정말 없는 경로(실제는 workspace/scripts/pipeline/). 검증 "health_check.py --check cron_status → exit code 0"은 존재하지 않는 옵션(실제 옵션은 --cron-check, --gateway-check 등) A + B
40276 타임아웃 패치 워커 반영 여부 missing_verification_exit_code 결론·근거·검증·근거파일 4줄이 과제 본문의 [자동 보강] 블록과 글자 그대로 동일. 검증은 "pytest" 한 단어 C(안내문 복사)
40282 같은 크론 비활성 원인 재진단 verification_command_unverifiable + missing_verification_exit_code 검증 "sqlite3 조회 결과 3회 이상 오류 기록 확인 … 행 수 3+ / exit code 0"(명령 리터럴 없음). 근거파일 "executions.db:5", "market_indicator_tracker.log:42 ERROR: ..."은 말줄임표 그대로의 가짜 인용. 과제 본문에 "(추정)"이라 스스로 적음 A

분류 기준: A=실행 안 한 값을 지어냄, B=워커 압축이 실존 근거를 지움, C=양식 설명문 복사. 집계: A 3건(40260·40275·40282), B 2건(40260·40275), C 1건(40276). 종료코드 누락 4/4건(100%).

같은 날 guardian의 형제 과제도 동일 계열이다: 40257(경로 두 개를 공백으로 붙여 써 failed), 40264·40265·40280·40287(failed). 과제 재발행이 원인 해소 없이 반복되고 있다.

3. 모델 경로 실측

  • 4건 직전 워커 로그: [model-fallback] agent=guardian model=openai-codex/gpt-5.4 failed (timeout=59s, remaining_budget=120s) (177020~177057, 177430~177464, 177500~177528 구간).
  • 40276 직전에는 gpt-5.4가 쿨다운 중이라 [model-cooldown] … 스킵 (1133s 남음) → 남은 후보는 ollama 소형모델뿐.
  • 모델 체인: scripts/shared/llm.py 151~156행 DEFAULT_MODEL_CHAIN = gpt-5.4 → nemotron(OpenRouter) → minimax(OpenRouter) → ollama/qwen3.5:4b. OpenRouter 2종은 로그상 "API key 미설정"으로 매번 스킵.
  • 패치 파일: scripts/agent_queue_worker.py 2128행 GATEWAY_MIN_MODEL_TIMEOUT_SEC = max(360, …), 2140행 _model_timeout_for, 3015행 "게이트웨이 타임아웃 뒤 소형 로컬 답변을 완료로 저장하지 않는다". 파일 수정시각 Sep 28 12:28:31 2026.
  • 실행 중 워커: ps -eo pid,lstart,command | grep agent_queue_worker.py → PID 46448~46462 8개 전부 2026년 9월 27일 07시 55분 09초 시작. 로그의 timeout=59s가 360초가 아닌 것 자체가 옛 코드가 돌고 있다는 증거다.

4. 원인 — guardian 프롬프트와 제출 기준에서 빠진 것

guardian 역할 프롬프트: scripts/agent_registry.py 311~336행.

빠진 것 현재 문구(원문) 결과
종료코드를 리터럴로 쓰라는 요구 333행 "검증: <실행한 검증 명령> → <실제 결과값/exit code>\n" — "결과값 또는 exit code"로 읽혀 선택사항이 됨 4/4건 missing_verification_exit_code. 제출 기준 문서는 이미 config/agent_completion_submission_standard.md 187행 "결과값에는 게이트가 읽는 리터럴 exit code 0을 그대로 쓴다"고 요구하지만 프롬프트에 없음
운영 장부의 정확한 절대경로 317~320행 "사용 가능 도구"에 health_check.py·ontology_core.py·curl 3개뿐. 크론 장부 경로 없음 guardian이 /Users/ron/.hermes/scripts/pipeline/…처럼 경로를 추측. AGENTS.md 최상단은 canonical을 /Users/ron/.hermes/cron/jobs.json·executions.db로 못박았지만 guardian 프롬프트엔 전달 안 됨
source_log_path:/verification_command: 리터럴 없음 (ron·codex·cowork 프롬프트에도 없음) 워커 _has_verifiable_four_block_completion_note()(agent_queue_worker.py 4207·4209행)가 두 리터럴을 요구 → 불충족 시 4324행 _public_result_text(… max_len=600) 압축 경로로 떨어져 절대경로·검증 줄 소실
[자동 보강] 설명문을 복사하지 말라는 금지 335행 "자기지시적 placeholder" 금지만 있음 — 어떤 문구가 placeholder인지 예시 없음 40276 복사 발생
부재 확인(0건) 시 grep 종료코드 규칙 없음 40260에서 "exit code 1 (찾음 없음)" 작성. 기준 문서 183~186행에 ! grep -rlq …; echo "exit code $?" 요령이 있으나 guardian에 미전달
실행할 수 없는 명령의 대체 수단 없음 과제는 stat -f, ps … \| grep, launchctl list \| grep, ls -lt … \| head를 요구했지만 워커 화이트리스트(agent_queue_worker.py 2213~2322행)에는 stat·ps·launchctl list(일반)가 없고, 2358행 dangerous_tokens가 \|를 전면 차단. guardian이 실행 못 한 명령을 "실행했다"고 서술

제출 기준 쪽 공백: - config/agent_completion_submission_standard.md §4 반려표(244~248행)에 missing_verification_exit_code의 guardian 사례와 "작은 대체모델 답변" 계열이 없다. 248행은 self_evolve.py phantom path만 다룬다. - 자동 보강 안내문 자체(scripts/orchestrator.py 1115·1491행, scripts/shared/agent_task_quality.py 1016행)가 "결론: …적는다." 형태로 답안 줄과 똑같은 라벨로 시작한다. 소형모델은 이를 채워야 할 빈칸이 아니라 완성된 답으로 인식한다.

5. 방지안 (우선순위)

1순위 — 워커 재시작으로 이미 만든 패치를 실제로 태운다 (운영 조치, 코드 수정 불필요)

  • 무엇: bash scripts/restart_agent_queue_workers.sh(화이트리스트 2306~2307행 등재) 또는 LaunchAgent kickstart로 8개 워커 재시작. 워커 재시작은 SOUL.md "확인 필요" 목록(삭제·배포·크론 비활성화 등)에 들지 않지만 운영 중 프로세스 교체이므로 ron이 수행하고 ops에 기록한다.
  • 왜: A분류(지어낸 값) 3건의 직접 조건인 "게이트웨이 59초 타임아웃 → 소형모델 답변"을 360초 최소 타임아웃 + 3015행 저장 차단이 막는다.
  • 기대효과: 재시작 후 guardian 로그의 timeout=59s 0건, 소형모델 답변의 done 시도 0건. guardian needs_evidence 중 A분류가 오늘 3/4 → 0 목표.

2순위 — guardian 프롬프트에 6줄 추가 (codex, §6 표 1행)

추가할 내용(문장 그대로 쓰는 것을 권장): 1. "검증: 줄 끝에는 반드시 exit code 0 리터럴을 적는다. exit=0, 통과, pytest 단어만으로는 반려된다." 2. "크론 장부는 /Users/ron/.hermes/cron/jobs.json, /Users/ron/.hermes/cron/executions.db만 쓴다. 파이프라인 스크립트는 /Users/ron/.hermes/workspace/scripts/pipeline/. /Users/ron/.hermes/scripts/pipeline/는 없다." 3. "모든 경로는 /Users/ron/로 시작하는 절대경로로, 한 줄에 하나씩 쓴다." 4. "완료 보고에 source_log_path: <절대경로>, verification_command: <실행한 명령> 두 줄을 넣는다." 5. "과제 본문의 [자동 보강] 블록은 작성 안내다. 그 문장을 결과에 복사하면 반려된다." 6. "실행하지 못한 명령(stat, ps, 파이프 |)은 실행했다고 쓰지 않는다. 대신 sqlite3, grep -n, ls, find로 확인하거나 '미실행'이라고 적고 done 대신 fail로 보고한다." - 기대효과: B분류(압축 소실) 2건은 4번 한 줄로 원문 통과 조건을 충족 → 0 목표. 종료코드 누락 4/4 → 0 목표.

3순위 — 안내문 복사를 게이트가 잡게 한다 (codex, §6 표 2·3행)

  • 결과 노트에 자동 보강 안내문(예: "수행한 일과 현재 상태를 완료형 한 문장으로 적는다", "각 경로 뒤에 실제로 연 줄 번호를 숫자로 붙이고")이 있으면 새 사유 completion_template_text_copied로 반려.
  • 안내문 자체를 답안 라벨과 겹치지 않게 바꾼다: "결론: …적는다." → "(작성 안내) 결론 줄에는 …". 라벨로 시작하지 않으면 복사해도 4블록 라벨 검사에서 먼저 걸린다.
  • 기대효과: C분류 재발 시 원인이 "exit code 누락"이 아니라 "안내문 복사"로 정확히 표시돼 재발행 과제가 헛돌지 않음.

4순위 — 과제 발행 쪽: guardian이 실행 못 하는 명령을 과제에 넣지 않는다 (codex, §6 표 4행)

  • 오케스트레이터가 guardian 과제 본문에 stat, ps, launchctl list |, | head, | grep를 넣으면 발행 전 경고하거나 허용 명령(sqlite3, grep -n, ls -la, find)으로 바꾼다. 또는 guardian 전용으로 stat -f, ps -eo 읽기전용 접두어를 화이트리스트에 추가(보안 검토 후).
  • 기대효과: "실행했다고 서술했지만 실행 흔적 없음" 유형 제거.

5순위 — 문서 반영 (cowork 후속, 코드 아님)

  • config/agent_completion_submission_standard.md §4 표에 missing_verification_exit_code(guardian 40260·40275·40276·40282), completion_template_text_copied(신설 예정) 두 행 추가.

6. codex용 코드 수정 대상 표

# 파일 줄 현재 바꿀 내용 검증 방법
1 /Users/ron/.hermes/workspace/scripts/agent_registry.py 317~320, 329~335 guardian 프롬프트: 도구 3개, 검증: <…/exit code> §5 2순위 6줄 추가. ron·analyst 프롬프트도 같은 <실제 결과값/exit code> 표현이므로 동일 적용 검토 grep -n "cron/executions.db" scripts/agent_registry.py 1건 이상, python3 -m py_compile scripts/agent_registry.py exit code 0
2 /Users/ron/.hermes/workspace/scripts/shared/public_result_formatter.py 3087 부근 completion_required_evidence_gaps() / 3063 completion_has_self_referential_placeholder_evidence() 옆 자동 보강 안내문 탐지 없음 안내문 고정 문구 목록 매칭 시 completion_template_text_copied 반환. scripts/server.py 740행 _completion_storage_required_result_gaps()와 worker 3812~3836행 라벨표에도 등록 40276 원문(guardian.log 177464행)을 입력한 회귀 테스트가 새 사유를 반환
3 /Users/ron/.hermes/workspace/scripts/orchestrator.py 1115·1491행, /Users/ron/.hermes/workspace/scripts/shared/agent_task_quality.py 1016·1020·1025·1031행 안내문이 "결론: …적는다."처럼 답안 라벨로 시작 "(작성 안내) 결론 줄: …" 형태로 라벨과 분리. scripts/tests/test_orchestrator_task_quality.py 456·487·763·1490행 기대값 동시 수정 python3 -m pytest scripts/tests/test_orchestrator_task_quality.py -q 전부 통과
4 /Users/ron/.hermes/workspace/scripts/agent_queue_worker.py 4187~4212 _has_verifiable_four_block_completion_note(), 4316~4326 압축 분기 source_log_path:/verification_command: 없으면 600자 압축 → 절대경로·검증 줄 소실 4블록 라벨 + 실존 절대경로 1개 이상 + exit code N이 있으면 두 리터럴 없어도 원문 보존. 최소한 압축 시 "검증:" 줄과 /Users/ron/ 경로는 보존 guardian.log 177458행(40275 원문)을 _sanitize_queue_update_payload("/complete", …)에 넣었을 때 /Users/ron/.hermes/cron/jobs.json과 "검증:" 줄이 남음
5 /Users/ron/.hermes/workspace/scripts/orchestrator.py (guardian 과제 생성부) 과제 본문 생성 경로 guardian 과제에 stat/ps/파이프 명령 포함 발행 전 guardian 실행 불가 명령 탐지 → 허용 명령으로 치환 또는 경고 오늘 과제 40260·40276 본문을 넣은 테스트에서 경고 발생
6 (코드 아님) 워커 재시작 — 8개 워커가 09-27 07:55 코드 bash scripts/restart_agent_queue_workers.sh ps -eo pid,lstart,command \| grep agent_queue_worker.py 시작시각이 12:28 이후, guardian 로그 timeout= 값 ≥ 360

7. 리스크

  • 단일 장애점: guardian·ron·analyst 모두 1순위 모델 하나(gpt-5.4)에 의존하고, 2·3순위(OpenRouter)는 키가 없어 사실상 빈 자리다. 1순위가 막히면 곧바로 4B 소형모델로 떨어진다. 1순위 패치가 적용돼도 "소형모델 답변을 저장 안 함 → 재시도 지연"으로 바뀔 뿐 처리량은 늘지 않는다. OpenRouter 키 설정 여부는 인증키 변경이라 해리 확인 사항이다.
  • 압축 경로 완화(표 4행)는 공개용 정제를 약화할 수 있다. 해리에게 가는 메시지로 절대경로가 새지 않도록 bus 전달 경로는 기존 정제를 유지해야 한다.
  • 재발행 과제가 원인 해소 없이 반복되면(오늘 guardian 형제 과제 최소 9건) 큐와 모델 예산만 소모한다. 표 1·6행 전에는 같은 주제 guardian 재발행을 멈추는 편이 낫다.

8. 검증방법

지표 명령 기대값 롤백 조건
워커 재시작 반영 ps -eo pid,lstart,command \| grep agent_queue_worker.py 8개 시작시각 > 2026-09-28 12:28 재시작 후 워커 수 < 8
게이트웨이 타임아웃 값 grep -n "agent=guardian model=openai-codex" /Users/ron/.hermes/logs/agent-queue-guardian.log 최근 줄 timeout= ≥ 360 —
guardian 반려율 sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT status,count(*) FROM bus_commands WHERE claimed_by='guardian' AND created_at>=datetime('now','-7 days','localtime') GROUP BY status" needs_evidence+failed 비율이 오늘 기준보다 50% 이상 감소 7일 뒤 감소 없음 → 표 4행 재검토
종료코드 누락 재발 grep -c '"agent": "guardian".*missing_verification_exit_code' /Users/ron/.hermes/workspace/logs/completion_evidence_blocked.jsonl 7일 증분 0 —
안내문 복사 grep -c "수행한 일과 현재 상태를 완료형" /Users/ron/.hermes/workspace/logs/completion_evidence_blocked.jsonl 증분 표 2행 반영 후 새 사유로만 기록 정상 보고 오탐 1건 이상 → 문구 목록 축소