needs_evidence 48건 반려 패턴 해석과 완료보고 개선안
needs_evidence 48건 반려 패턴 해석과 완료보고 개선안
조회: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db (bus_commands, status='needs_evidence'), 2026-10-17 12:26~12:45 KST. 코드와 DB는 수정하지 않았다. 건수 집계가 아니라 "왜 이 형태로 반복되는가" 해석이 목적이다. 반려 사유 원문은 logs/completion_evidence_blocked.jsonl의 reasons 필드(48건 전부 id로 매칭됨)를 함께 읽었다.
[권고]
1순위 — 검증 줄의 exit code 표기 불일치를 표준문서·프롬프트에서 제거한다 (실행 결과 누락 20건, 48건 중 41.7%).
- 왜: 서버 게이트는 검증:/검증 결과:/테스트 결과:로 시작하는 줄에서 exit code <숫자> 리터럴만 읽는다. → exit 0, exit=0, 6행 반환, 통과는 모두 걸리지 않는다(정규식 복제 실험: '→ exit 0' → None, '6행 반환, 통과' → None, '6행 반환, exit code 0' → 0). 그런데 표준문서 §3 112행 검증: ... → <실제 결과값 또는 exit code>는 exit code를 "또는"으로 선택사항처럼 가르치고, §9-3-1 guardian 템플릿 예시는 → exit 1(579행)·→ exit 0(450행)로 게이트가 못 읽는 표기를 쓴다. scripts/orchestrator.py:812도 → exit 0을 예시로 준다.
- 기대효과: guardian exit code 계열 18건이 사라지는 방향. 목표는 신규 *_verification_exit_code 반려 7일간 0~1건.
2순위 — 반려 집계·분류형 태스크의 재발행 루프를 끊는다 (48건 중 33건, 68.8%가 제목에 needs_evidence/반려가 들어간 집계 태스크).
- 왜: 집계 태스크가 반려되면 그 반려가 다시 집계 대상이 되고, 오케스트레이터가 같은 집계를 다시 발행한다(31건이 requested_by=orchestrator). 400-reports에 이름에 needs_evidence가 든 보고서가 이미 26개 있는데도 27번째가 나오는 구조다. 이번 CMD#41904도 이 유형이다.
- 기대효과: 신규 needs_evidence 유입 중 집계형 0건. 건수 증가 속도(최근 20일 약 2.4건/일 → 0.5건/일 미만)를 낮춘다.
3순위 — 저장 시 원문 손실 때문에 4블록 준수 여부를 DB만으로 감사할 수 없다는 점을 보완한다 (48건 전부 result_note 360~486자).
[현황]
1) claimed_by × 차단 사유 (표는 사유 중복 포함, 행수 합 48)
사유 분류 기준(completion_evidence_blocked.jsonl reasons 기준): 검증 근거 미기재 = verification_command 계열·"검증 근거 없음"; 핵심 근거 없음 = "결과물과 검증 근거가 없음"·"결과 확인 보류"·missing_result_or_verification·completion_result_note_forces_rework; 실행 결과 누락 = missing/invalid_verification_exit_code; 결과물 경로 누락 = missing_execution_artifact·execution_artifact_path_not_found. 미분류 0건.
| claimed_by | 행수 | 검증 근거 미기재 | 핵심 근거 없음 | 실행 결과 누락 | 결과물 경로 누락 |
|---|---|---|---|---|---|
| guardian | 32 | 12 | 14 | 18 | 3 |
| cowork | 6 | 1 | 4 | 1 | 0 |
| analyst-macro | 5 | 0 | 5 | 0 | 0 |
| analyst-pm | 4 | 0 | 4 | 0 | 0 |
| ron | 1 | 0 | 0 | 1 | 0 |
| 합계(중복 포함) | 48 | 13 | 27 | 20 | 3 |
- guardian이 32/48 = 66.7%. 상위 2개 조합은 guardian×실행 결과 누락(18건)과 guardian×핵심 근거 없음(14건).
- 행 안의
보완 필요: 결과물 경로, 원문 확인, 검증 명령·결과.문구는 48건 전부 동일하다. 실제 사유가 달라도 항상 붙는 고정 문구라서 "결과물 경로 누락"은 사유 정보가 아니다. 진짜 경로 문제는 3건뿐이다. - invalid_verification_exit_code 7건의 마지막 발생은 2026-10-11T01:59Z(#41420)다. 같은 계열 중 서버 게이트 정규식에 왼쪽 경계가 들어간 뒤(
scripts/server.py:734의(?<![A-Za-z_]))로는 새 건이 없다.missing_verification_exit_code 15건이exit_code 15로 읽히던 오탐이 고쳐진 결과로 해석된다(추정: 패치 시각은 로그로 확인하지 못함).
2) 상위 조합 A — guardian × 실행 결과 누락 (18건)
대표 id: #40346, #41385.
-
40346 (제목 "옛 경로 mcp_health_server.py·stealth-browser 중복 기동 조회 전용 점검", 차단 사유 missing_verification_exit_code) 저장 원문: "결론: 옛 경로 MCP 헬스서버 8개와 stealth-browser 2개가 중복된 원인을 ... PID별 표를 /Users/ron/.hermes/workspace/logs/guardian_legacy_mcp_dup_20260929.md 에 저장했다. 조회만 했으며 ... 근거:
ps -ax" (여기서 잘림). -
41385 (제목 "needs_evidence 27건 에이전트별·사유별 집계 (4블록 증거 포함 재발행)", 차단 사유 invalid_verification_exit_code) 저장 원문: "결론: needs_evidence 27건을 에이전트별·사유별로 집계해 md 파일로 저장했습니다. ... 근거: 아래 sqlite3 명령을 2026-10-11 00:40 KST에 실행했고, 6개 행이 반환됐습니다. - 명령:
sqlite3 -readonly ..." (여기서 잘림).
현행 config/agent_completion_submission_standard.md와 어긋나는 지점:
1. 표준 §3(112행)은 검증 값을 "실제 결과값 또는 exit code"로 쓰게 한다. 두 건 모두 결과를 "6개 행이 반환됐습니다"처럼 건수로만 적었고, 이는 표준 문구 그대로의 준수다. 그러나 게이트(scripts/server.py:814-839)는 검증: 줄에 exit code <숫자>가 있어야 통과시킨다. 표준 문서 안에서 exit code 0 리터럴 의무는 부재 확인 소절(187행)과 analyst-fundamental 소절(653~660행)에만 명시되어 있어, §3 일반 규칙과 게이트 사이에 틈이 있다.
2. #40346은 근거 줄에서 잘려 검증 줄 자체가 저장본에 없다. 표준 §0 체크 2번("검증 값 안의 수치/exit code가 그 줄 안에서 바로 읽히는가")을 지켰는지 저장본으로는 확인할 수 없다(미확인).
3. #41385의 차단 사유는 missing이 아니라 invalid다. 저장본에서는 어느 숫자가 exit code로 읽혔는지 보이지 않는다(원인 미확인). 로그 note_preview(240자)도 같은 위치에서 끊긴다.
4. 표준 §9-3-1 guardian 템플릿(579행 → exit 1, cron_ok=True ...)은 비정상 종료 예시를 그대로 보여 준다. 이 표기를 따라 쓰면 exit 1은 정규식에 안 걸려 missing, exit code 1이면 invalid로 반려된다. 따라 쓰면 반려되는 예시다.
3) 상위 조합 B — guardian × 핵심 근거 없음 (14건, 3건은 다른 사유와 중복)
대표 id: #40825, #41101.
-
40825 (제목 "오늘 failed 4건·needs_evidence 7건 반려 사유 분류", 차단 사유 "결과 확인 보류") 저장 원문: "결론: 11건(failed 4, needs_evidence 7)을 모두 분류했습니다. 인프라 실패(llm_infra_failure)는 0건이고 전부 증거 게이트 반려(4블록 미비)입니다. ... 분류 결과 (에이전트별 건수): - guar" (여기서 잘림).
-
41101 (제목 "최근 24시간 needs_evidence·failed 태스크 원인 분류 (SQL 집계)", 차단 사유 "결과 확인 보류") 저장 원문: "결론: ... 3종으로 분류했다. 코드는 수정하지 않았다. 근거:
sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db로 bus_commands를 조회했다(2026-10-07 14:18 KST). 24" (여기서 잘림).
어긋나는 지점:
1. 두 건 모두 결론:과 근거:는 있고 검증:·근거파일:이 저장본에 없다. 표준 §3(101~114행)은 4블록을 "이 순서 그대로" 쓰라 하고, agent_registry.py guardian 블록(333~342행)도 "4블록을 본문보다 먼저 쓰고 900자 이내"를 요구한다. 두 건 모두 220자 발췌가 근거: 줄 중간에서 끊겨, 그 뒤 라벨이 실제로 없었는지 잘렸는지는 저장본으로 구분되지 않는다(미확인).
2. #40825는 결론 문장에 "전부 증거 게이트 반려(4블록 미비)"라는 분석이 들어간다. 표준 §3-1은 결론을 "실행/확인/작성한 대상 + 완료 동사"로 쓰라 하고, 분석 판단은 근거 쪽에 둔다.
3. 표준 §9-3-1의 금지 규칙(게이트 반려 로그를 근거파일로 인용 금지)은 지켜졌는지 저장본으로 확인되지 않는다. 같은 유형의 집계 태스크에서 반려 로그를 근거로 삼는 경향이 보고되어 있으나 이번 두 건에서는 미확인이다.
4) 그 밖에 해석에서 새로 나온 사실
- 태스크 본문에 붙는 자동 보강 문구(
[자동 보강: 완료보고 4블록 설명형 라벨 보충], 생성 위치scripts/shared/agent_task_quality.py:1016)가 에이전트에게 그대로 복사된 사례가 1건 있다. #40276(guardian)의 저장 원문은결론: 수행한 일과 현재 상태를 완료형 한 문장으로 적는다.로 시작한다. 즉 지시문의 자리표시 문장을 결과로 제출했다. 이번 작업 본문에도 같은 보강 문구가 붙어 있다. - 같은 보강 문구의 검증 줄 기본값은
exit code 0; 통과(#41233 본문에서 확인), 4블록 지시문은 "exit code 0 또는 통과"(agent_task_quality.py:1004)라고 가르친다. 그런데 서버 게이트에서통과는검증 결과:/테스트 결과:/pytest 뒤에 붙을 때만 인정된다(scripts/server.py:829-834).검증: ... 6행 반환, 통과는 걸리지 않는다(실험 확인). 지시문이 게이트보다 느슨하다. - analyst-macro 5건은 전부 "미국 IG·HY OAS 파일 기반 점검" 계열 태스크이고(analyst-pm은 4건 중 1건만 OAS 제목), analyst 9건의 사유는 전부 missing_result_or_verification이다. 저장본에는
결론:·근거:라벨은 있고검증:줄이 없다(예: #41769 저장 원문 "결론: BAMLC0A0CM과 BAMLH0A0HYM2의 최신값 ... 3줄 요약까지 마쳤습니다. 근거: HY−IG 차이는 5일간 +13bp, 20일간 +37bp 확대됐습니다. - 해석: ..."). 근거 줄에 소스 파일 경로 대신 해석 문장이 들어가 있다. - guardian 전용 4블록 규칙(
agent_registry.py:311~345, 최종 수정 2026-10-14 06:10)이 들어간 뒤에도 guardian의 신규 반려가 2건(#41897 exit code 누락, #41902 검증 근거 없음) 나왔다. 표본이 2건이라 효과 없음으로 단정하지 않는다. 다만 analyst-macro(436행)·analyst-pm(513행 부근)·cowork·ron 프롬프트에는 공통 4줄 형식만 있고 이 guardian 전용 보강 문단은 없다. 해당 프롬프트가 실제 워커에 주입되는지는 확인하지 못했다(미확인).
수정 필요 지점 (직접 수정하지 않음, codex 위임 대상)
scripts/orchestrator.py:812— 검증 예시'python3 scripts/health_check.py → exit 0'를→ exit code 0으로. 게이트 정규식이exit 0을 못 읽는다.scripts/shared/agent_task_quality.py:1004,:1022-1026— 지시문의 "exit code 0 또는 통과"를 "검증 줄에exit code 0리터럴 필수"로 통일하고, 기본 검증 값이 자리표시 문장을 그대로 복사하게 만들지 않도록 "값 자리" 표기로 교체. #40276의 자리표시 복사 재발 방지.scripts/agent_registry.pyron(182행~)·cowork(272행~)·analyst-macro(394행~)·analyst-pm(485행~) 블록 — guardian 블록(333~342행)의 "검증 한 줄에 명령·결과 수치·exit code, 4블록 우선·900자" 문단을 해당 프롬프트에도 복제. 단, 실제 주입 여부를 먼저 확인할 것.scripts/shared/public_result_formatter.py:3933부근_completion_raw_excerpt(max_len=220)와:3985~4010래퍼 — 저장 원문이 220자로 잘려검증:·근거파일:이 저장본에서 사라진다. 감사용으로 원문을 별도 필드나 파일에 남기도록 변경 검토. 지금은 어느 라벨이 실제로 빠졌는지 DB로 증명할 수 없다.- 오케스트레이터의 반려 집계 태스크 발행 지점 — 발행 코드는
grep needs_evidence scripts/orchestrator.py로 찾지 못했다(4199행의 재제출 체인 닫기 로직은 형식 반려만 대상). 같은 목적의 보고서가 이미 있는지(400-reports/*needs_evidence*26개) 확인하고, 있으면 발행을 보류하는 규칙 추가. 발행 위치는 미확인이므로 codex가 먼저 찾아야 한다.
문서 수정 제안(config/ 아래, 이번 작업에서는 수정하지 않음): agent_completion_submission_standard.md §3 112행 "또는 exit code"를 "그리고 exit code 0 리터럴"로, §9-3-1의 450행·579행 예시를 exit code 0/exit code 1 리터럴 표기로(비정상 예시는 "이 경우 반려됨" 주석 추가).
[리스크]
- 단일 장애점: 반려 집계 태스크가 자기 자신을 반려시키는 루프. guardian이 다시 집계를 맡는 한 같은 방식으로 반려된다(CMD#41897→#41902→#41904 연속).
- 의존관계: 표준문서 예시를 고쳐도
orchestrator.py:812와agent_task_quality.py:1004가 따로 같은 잘못된 표기를 가르치면 에이전트는 마지막에 읽은 쪽을 따른다. 세 곳을 같이 고쳐야 한다. - 데이터 한계: result_note가 220자 발췌라서 본 문서의 "라벨 누락" 판단은
completion_evidence_blocked.jsonl의 reasons와 저장본 앞부분에 근거한다. 실제 제출 원문 전체는 확인하지 못했다. - 오탐 위험: 사유 분류는 reasons 문자열 규칙 기반이다. 사유가 2~3개 겹친 행이 많아 표 합계(63)가 행수(48)보다 크다.
[검증방법]
- 지표: 신규 needs_evidence 중
*_verification_exit_code계열 비율. 현재 20/48 = 41.7%. 수정 후 7일간 신규 건의 10% 미만이면 성공. - 명령:
sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT claimed_by,COUNT(*) FROM bus_commands WHERE status='needs_evidence' AND updated_at>='<수정일>' GROUP BY claimed_by"— 기대값: guardian 비중 66.7% → 40% 미만. - 집계형 태스크 유입: 같은 쿼리에
AND title LIKE '%needs_evidence%'추가, 기대값 0건. - 롤백 조건: 수정 후 정상 완료 건이
missing_*외 새 사유(예: 정규식 과엄격화)로 반려되면 해당 수정을 되돌린다.