virtual-insanity
← 리포트 목록

오늘 guardian 결과 보류 5건 반려 사유 분류와 중복 진단 발행 방지안

2026-09-28 cowork [ops, completion-gate, needs_evidence, guardian, orchestrator, dedup]

오늘 guardian 결과 보류 5건 반려 사유 분류와 중복 진단 발행 방지안

작성: Cowork, 2026-09-28 22:01~22:20 KST. 조회 전용 — 코드·크론·DB는 바꾸지 않았다.

[권고]

  • 1순위: 오케스트레이터 발행 게이트에 "운영 주제 키(topic_key)" 기반 일일 상한(같은 주제 guardian 진단 하루 3회, 종결 보고서가 있으면 0회)을 추가한다. 오늘 같은 주제 진단이 guardian 4건 + cowork 1건 + ron 1건, 총 6건 발행됐고 guardian 4건은 전부 needs_evidence로 끝났다. 적용 시 오늘 기준 guardian 재발행 4건 → 최대 3건, ron 종결(21:12) 이후 발행은 0건이 기대값이다.
  • 2순위: guardian 완료 보고의 "검증:" 줄에 exit code N 리터럴을 강제한다(guardian 역할 프롬프트 + 태스크 본문 자동보강 문구 수정). 오늘 5건 전부(100%)가 missing_verification_exit_code를 포함했다. 서버 저장 게이트는 에이전트 구분 없이 exit code N/rc=N 또는 "검증 결과 … 통과/passed"만 인정하므로, 문구 한 줄만 맞추면 5건 중 최소 2건(#40260, #40276)은 해당 사유가 해소됐을 것이다.
  • 3순위: 태스크 본문 자동보강 템플릿 문구("결론: 수행한 일과 현재 상태를 완료형 한 문장으로 적는다.")를 에이전트가 그대로 베끼지 못하도록 서버 게이트에 "템플릿 문구 복사" 반려 라벨을 추가한다(#40276 재현).

[현황]

조회: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT id,title,result_note FROM bus_commands WHERE id IN (40260,40275,40276,40282,40301)" → 5행, 2026-09-28 22:01 KST. 5건 모두 status=needs_evidence, claimed_by=guardian, requested_by=orchestrator. 오늘 guardian 태스크 전체는 6건(done 1, needs_evidence 5) → 결과 보류율 83%.

반려 원문 로그: ~/.hermes/workspace/logs/completion_evidence_blocked.jsonl(5건 모두 surface=server.close_bus_command).

표 1. 반려 사유 분류

명령 발행 시각 주제 반려 사유(원문) 같은 주제 반복 템플릿 베낌 보고서 실존
#40260 09:47 macro-timeseries(BAMLC0A0CM.json) 갱신 주체 missing_verification_exit_code 예(1회차) 아니오 260928_guardian_bamlc0a0cm-writer.md(11:17)
#40275 13:07 market-indicator-tracker 08-16 정지 원인 execution_artifact_path_not_found(jobs.json·executions.db·market_indicator_tracker.py를 워크스페이스 기준 상대경로로 적음), missing_verification_exit_code 예(2회차) 아니오 260928_guardian_market-indicator-tracker-stall.md(13:12)
#40276 13:07 워커 모델 타임아웃 패치 반영 여부 missing_verification_exit_code 아니오(별개 주제) 예 — 결론 줄이 템플릿 원문 그대로 없음
#40282 13:41 같은 크론 08-16 비활성 원인("실제 경로로 재발행") verification_command_unverifiable, missing_verification_exit_code 예(3회차, #40275 재발행) 아니오 위와 동일 보고서 공유
#40301 17:52 context-macro.json 07-20 정지 / vault-macro-bridge missing_execution_artifact, missing_verification_command, missing_verification_exit_code 예(4회차, 인접 주제) 아니오 260928_guardian_context-fundamental-technical-stall.md(19:55)

사유별 건수(5건 중 중복 집계): missing_verification_exit_code 5건(100%), execution_artifact_path_not_found 1건, verification_command_unverifiable 1건, missing_execution_artifact 1건, missing_verification_command 1건. 사유가 1개뿐인 건 2건(#40260, #40276), 2개 2건, 3개 1건.

같은 주제 흐름: #40260(09:47) → #40275(13:07) → #40282(13:41, 제목에 "재발행" 명시) → #40301(17:52) → cowork #40318(20:35, failed) → ron #40320(21:08, done 21:12, 종결 보고서 260928_ron_paused-collector-cron-reenable-approval.md). 즉 guardian 4회 + 정리 2회 = 하루 6회 같은 주제 발행.

템플릿 베낌 사례

  • 40276 결론 줄 = "결론: 수행한 일과 현재 상태를 완료형 한 문장으로 적는다." 이 문구는 태스크 본문 자동보강 템플릿 원문이다(scripts/orchestrator.py:1115, scripts/orchestrator.py:1491, scripts/shared/agent_task_quality.py:1016). 같은 태스크 본문에는 "검증: pytest"만 있고 결과값 자리가 비어 있었고(본문 1174자 위치부터 확인), guardian은 명령·결과 없이 이 본문을 거의 그대로 복사해 제출했다.

  • 5건 모두 본문에 같은 템플릿 문구가 들어 있었다(instr 위치 1174~1615자). 베낀 건은 #40276 1건이지만 노출은 5건 전부다.

[원인 가설] guardian 결과에서 exit code가 왜 인식되지 않았나

판정 코드 위치(실측): - scripts/agent_queue_worker.py:7142~7144 — exit_match = _CODEX_VERIFICATION_EXIT_CODE_RE.search(value) or ...search(raw) 가 없으면 return False, "missing_verification_exit_code". 인식 정규식은 scripts/agent_queue_worker.py:7024~7026: (?:exit\s*code|exit_code|return\s*code|returncode|\brc\b)\s*[::=]?\s*(?P<code>\d+) — 즉 "exit code", "rc" 같은 단어 + 숫자가 있어야만 잡힌다. - 단, 이 7144행 함수는 scripts/agent_queue_worker.py:7385 if agent_key == "codex": 분기에서만 호출된다. guardian은 else 분기(7387~7400행)로 가서 "검증 결과 … 통과" 같은 관측 문구만 있어도 워커 단계는 통과한다. - 실제 5건을 막은 곳은 서버 저장 게이트다(로그 surface=server.close_bus_command). scripts/server.py:746 _completion_storage_required_result_gaps 가 에이전트 구분 없이 scripts/server.py:798 에서 _STORAGE_EXIT_CODE_RE(720~723행, 워커 7024행과 같은 정규식)로 찾고, 못 찾으면 "검증 결과/테스트 결과/pytest … 통과/passed" 보조 패턴을 보고, 둘 다 없으면 scripts/server.py:807 에서 missing_verification_exit_code를 붙인다.

가설(건별 근거): 1. "exit code" 단어 없이 숫자만 적음 — #40301 "python3 (수동 실행) → 0". 화살표 뒤 숫자 0만 있고 "exit code"/"rc" 단어가 없어 정규식 불일치. (확인됨, 로그 미리보기 원문) 2. 검증 명령만 있고 결과값이 없음 — #40275 "검증 명령: python3 scripts/health_check.py", #40276 "검증: pytest". 결과 자체가 비어 있으니 인식할 숫자가 없음. (확인됨) 3. 검증 자리에 조회 명령을 서술형으로 적음 — #40260 "stat -f '%Sm' memory/macro-timeseries/BAMLC0A0CM.json"을 근거 줄에만 적고 검증 줄·결과값 없음. (확인됨) 4. 경로가 빈칸으로 사라진 채 제출됨 — #40260 "1. 에서", #40301 "생성된 와 파일 mtime", "근거파일: :14". 절대경로 자리가 공백으로 비어 있다. guardian 쪽 출력 정리 과정에서 경로 토큰이 지워졌을 가능성이 있다(미확인 — 이번 조사에서 해당 삭제 지점을 찾지 못함. 워커 scripts/agent_queue_worker.py:7488의 <원본로그경로> 치환은 발췌용이라 해당 아님). 이 때문에 #40301이 missing_execution_artifact·missing_verification_command까지 겹쳤다. 5. 상대경로 기준 불일치 — #40275 "jobs.json, executions.db"를 파일명만 적어 서버가 /Users/ron/.hermes/workspace/jobs.json으로 해석 → 실존 경로는 /Users/ron/.hermes/cron/jobs.json이라 path_not_found. (확인됨) 6. 공통 배경 — guardian 역할 프롬프트·자동보강 템플릿이 "결과값(exit code 0 또는 통과)"이라고 안내해, guardian이 "통과"만 쓰거나 아예 비워도 된다고 해석할 여지가 있다. 서버는 "통과"를 "검증 결과/테스트 결과/pytest" 라벨 뒤에서만 인정하므로 "검증: … 통과"는 인정 폭이 좁다(서버 798~806행).

[개선안] 같은 주제 guardian 진단 하루 3회 초과 재발행 방지 (codex 위임용 수정 지점)

현재 방어선이 이 사례를 못 막은 이유: - scripts/orchestrator.py:363 DUPLICATE_DISPATCH_WINDOW_MIN=15 + 2553~2569행: 제목·대상이 정확히 같고 15분 안일 때만 차단. 오늘 4건은 제목이 매번 달랐고 간격도 34분~4시간 40분. - scripts/orchestrator.py:379~383 MACRO_EVENT_KEYWORDS(호르무즈·중국관세·FOMC 3개만)와 2582~2602행 이벤트 게이트: 운영 주제(수집 크론 정지, macro-timeseries)는 키워드에 없어 게이트 대상 자체가 아님. - needs_evidence는 종결 상태로 취급되지 않아, 오케스트레이터가 "미해결"로 보고 새 제목으로 재발행(#40282 제목 "실제 경로로 재발행").

codex 위임 수정 지점: 1. scripts/orchestrator.py:379 부근 — MACRO_EVENT_KEYWORDS와 별도로 운영 주제 사전 OPS_TOPIC_KEYWORDS 신설. 예: paused_collector_cron: ["market-indicator-tracker", "수집 크론", "macro-timeseries", "BAMLC0A0CM", "context-macro", "vault-macro-bridge", "08-16"]. 2. scripts/orchestrator.py:2284 create_task() 안, 2572행(이벤트 게이트) 바로 앞 — topic_key가 잡히면 (a) 같은 topic_key 마커를 가진 오늘 발행 건수를 상태 무관(needs_evidence·failed 포함)으로 세어 target_agent별 3건 이상이면 OPS_TOPIC_DAILY_CAP_BLOCKED 로그 후 return False. (b) 기존 이벤트 게이트처럼 body 앞에 [TOPIC:xxx] 마커를 심어 스키마 변경 없이 집계. 상한값은 환경변수 ORCH_OPS_TOPIC_DAILY_CAP(기본 3). 3. 같은 함수 — 종결 보고서 우선 규칙: ~/knowledge-agent/400-reports/YYMMDD_*에 오늘 날짜 + 같은 topic 태그를 가진 ron/cowork 종결 보고서(예: 260928_ron_paused-collector-cron-reenable-approval.md)가 있거나, 같은 topic_key의 done 건이 있으면 guardian 신규 진단 발행을 0건으로 막고, 필요 시 "승인 대기" 한 줄만 ops에 남긴다. 4. scripts/orchestrator.py:2588 — 기존 이벤트 게이트의 status NOT IN ('failed','cancelled') 조건은 needs_evidence를 이미 "활성"으로 센다. 새 topic 게이트는 failed까지 포함해 세야 한다(실패 후 새 제목 재발행이 오늘 패턴이었음). 5. 회귀 테스트 제안: scripts/tests/test_orchestrator_task_quality.py 옆에 오늘 4건 제목을 픽스처로 넣어 4번째 발행이 막히는지, ron 종결 뒤 guardian 발행이 막히는지 확인.

2순위(보고 형식) codex 위임 수정 지점: - scripts/orchestrator.py:1117 과 scripts/shared/agent_task_quality.py:1016~1024 자동보강 문구의 검증 줄을 "검증: <명령> → exit code <숫자>" 형식 예시로 바꾸고, 결론 줄 템플릿은 "결론: (여기에 실제 완료 사실)" 같은 채움 표시로 바꾼다. - scripts/server.py:746 게이트에 템플릿 원문 복사 감지 라벨(예: template_placeholder_copied) 추가 — #40276 재현 케이스를 테스트로.

[리스크]

  • topic 키워드 과대 매칭: "macro-timeseries"는 analyst-macro 정상 분석 태스크에도 들어간다. 상한은 target_agent=guardian(진단류)에만 우선 적용해야 오탐 차단을 줄인다.
  • 종결 보고서 규칙은 파일명·태그 규칙에 기대는 단일 장애점이다. 보고서 태그가 빠지면 게이트가 fail-open 된다(기존 dedup과 같은 fail-open 정책 유지 권장).
  • 경로 공백 문제(가설 4)는 원인 지점 미확인이다. 발행 상한만 고치면 재발행은 줄지만 guardian 결과 보류율 자체는 그대로 남는다.

[검증방법]

  • 발행 억제: 다음 날 sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT count(*) FROM bus_commands WHERE target_agent='guardian' AND date(created_at)=date('now','localtime') AND body LIKE '[TOPIC:paused_collector_cron]%'" → 기대값 3 이하. 오케스트레이터 로그에 OPS_TOPIC_DAILY_CAP_BLOCKED 1회 이상.
  • 보고 형식: logs/completion_evidence_blocked.jsonl에서 agent=guardian·missing_verification_exit_code 건수, 오늘 5건 → 주간 평균 1건 이하 목표. guardian 결과 보류율 83% → 30% 이하.
  • 롤백 조건: 정상 guardian 헬스체크(주제 무관)가 topic 게이트에 막힌 사례 1건이라도 나오면 ORCH_OPS_TOPIC_DAILY_CAP를 크게 올려 사실상 해제.

완료 보고 4블록

결론: 오늘 guardian 결과 보류 5건의 반려 사유를 표로 분류하고, 실제 차단 지점이 서버 저장 게이트(server.py 807행)임을 확인했으며, 같은 주제 하루 3회 초과 재발행 방지안을 codex 수정 지점과 함께 작성했다. 근거: sqlite3 조회 5행(2026-09-28 22:01 KST), logs/completion_evidence_blocked.jsonl 5건 surface=server.close_bus_command, scripts/agent_queue_worker.py:7144 "return False, \"missing_verification_exit_code\"", scripts/server.py:807. 검증: grep -c '^결론:|^근거:|^검증:|^근거파일:' /Users/ron/knowledge-agent/400-reports/260928_cowork_guardian-needs-evidence-triage.md → 4, exit code 0 근거파일: /Users/ron/knowledge-agent/400-reports/260928_cowork_guardian-needs-evidence-triage.md