guardian 완료보고 needs_evidence 반복 반려 방지안 (실DB 분포 기반)
guardian 완료보고 needs_evidence 반복 반려 방지안
조회 기준: /Users/ron/.hermes/data/ops_multiagent.db, sqlite3 -readonly, 2026-10-06 04:19~04:22 KST. 코드 수정 없음. 이 문서는 제안서이며 코드 수정은 끝의 codex 위임 후보로만 남긴다.
1. [권고]
1순위 — 오케스트레이터가 guardian용 body를 만들 때 "검증:" 줄 문구를 고정 문장으로 박는다.
- 무엇을: "검증: <재실행 명령 전체> → <결과값>, exit code: 0" 한 줄 형태를 body 끝 자동보강 블록에 넣는다. 특히 exit code: 0을 콜론 포함 리터럴로, 같은 줄에 쓰게 한다.
- 왜: 최근 7일 live 반려 7건 중 5건(71.4%)이 exit code 계열이다(missing_verification_exit_code 4건, invalid_verification_exit_code 1건). 현재 자동보강 문구(orchestrator.py 1115~1117행)는 "검증: 실제 검증 명령(py_compile 또는 pytest 등)과 결과값(exit code 0 또는 통과)을 적는다"여서, guardian이 "통과"만 쓰거나 exit code를 서술문 속에 흩어 쓴다.
- 기대효과: live 7건 중 5건(71.4%)에 해당하는 사유가 사라진다. 목표는 guardian 7일 exit code 계열 반려 5건 → 0~1건.
2순위 — 분류표 작성형 태스크 body에 "라벨 이름을 검증 줄에 쓰지 말 것"과 "존재하는 경로만 쓸 것" 두 문장을 넣는다.
- 왜 (a): 반려 사유를 집계하는 태스크는 결과에 missing_verification_exit_code: 4건 같은 라벨 건수를 적는다. 게이트 정규식(agent_queue_worker.py 7022~7029행)의 exit_code[::=]\s*(?P<code>...) 가 그 문자열을 실제 exit code로 읽는다. #40854의 result_note에서 정규식이 잡은 문자열은 exit_code: 4건 (guardian #40276, ...)였고, 이 값은 0이 아니므로 invalid_verification_exit_code가 된다. 이 재현은 아래 4절에 있다.
- 왜 (b): 7일 archive 포함 172건 중 execution_artifact_path_not_found가 89건(51.7%), verification_command_unverifiable이 64건(37.2%)이다. 인용된 실패 경로의 대다수는 /Users/ron/.hermes/workspace/jobs.json이다(게이트 로그 195건 중 74건). 실제 파일은 /Users/ron/.hermes/cron/jobs.json이다. 확인: ls /Users/ron/.hermes/workspace/jobs.json → No such file, ls /Users/ron/.hermes/cron/jobs.json → 존재.
- 기대효과: archive 포함 not_found 89건, unverifiable 64건 중 jobs.json 단일 경로 비중 74건이 사라질 수 있다.
2. [현황] 실DB 분포
2-1. 대상 모집단
- live
bus_commands, claimed_by='guardian', status IN (needs_evidence, failed), 최근 7일: 7행(needs_evidence 6, failed 1). - 같은 조건의
bus_commands_archive: 165행(전부 failed). 합계 172행. - live 7행의 id: 40633, 40825, 40842, 40854, 40869, 40902, 40949.
2-2. 사유 라벨별 건수
live 7행(result_note의 "차단 사유/누락·차단" 구간 기준, 한 건에 라벨이 여러 개일 수 있음):
| 라벨 | 건수 | 대상 id |
|---|---|---|
| missing_verification_exit_code | 4 | 40633, 40842, 40854, 40902 |
| invalid_verification_exit_code | 1 | 40869 |
| missing_execution_artifact | 1 | 40842 |
| missing_verification_command | 1 | 40842 |
| execution_artifact_path_not_found | 1 | 40949 |
| verification_command_unverifiable | 1 | 40949 |
| 라벨 없음("결과 확인 보류"만 표시) | 1 | 40825 |
live+archive 172행(라벨 포함 건수): execution_artifact_path_not_found 89, llm_infra_failure 66, verification_command_unverifiable 64, missing_source 16, 라벨없음 14, missing_verification_result 5, missing_verification_exit_code 4, missing_execution_artifact 4, missing_verification_command 4. 합계는 건수가 아닌 라벨 수다. 자동 분류 정규식 기반이라 ±몇 건 오차가 있을 수 있다.
2-3. 태스크 유형별 (제목 키워드로 분류, 172행)
- 주간 크레딧 요약 크론 점검형: 86건(50.0%). 대부분 archive의 failed. 라벨: not_found 53, llm_infra_failure 51, unverifiable 24.
- 반려 분류표 작성형: 69건(40.1%). 라벨: unverifiable 35, not_found 27, llm_infra_failure 13, exit code 계열 4.
- macro-timeseries 점검형: 9건, 지식사랑방 점검형: 4건, 기타 4건.
- live 7행만 보면 분류표 작성형 5건(40825, 40842, 40854, 40869, 40902), 지식사랑방 1건(40633), 분류표 failed 1건(40949)이다.
2-4. 24시간 비중에 대한 주의
- 작업 설명의 "24시간 8건 중 guardian 7건(87.5%)"은 #40950의 03:05 KST 집계와 일치한다(live+archive 합산).
- 04:22 KST에 live 테이블만
updated_at >= datetime('now','localtime','-24 hours')로 재조회하면 guardian 3건(failed 1, needs_evidence 2), analyst-pm 3건, cowork 2건으로 보인다. archive 포함 여부와 창 이동으로 비율이 달라진다. 이 문서의 7일 분포가 비율 변동에 덜 민감하다.
3. 실제 result_note 대조 — 어느 유형에서 어떤 필드가 빠지나
대조 방식: DB의 result_note는 게이트가 덮어쓴 문구여서 원문이 잘려 있다. 원문은 /Users/ron/.hermes/logs/agent-queue-guardian.log의 command_id별 JSON note에서 확인했다.
유형 A: 반려 분류표 작성형 — "검증:" 줄에서 exit code가 누락되거나 잘못 잡힘
-
40633 (지식사랑방 원인 분류): 검증 줄 원문 "위 조회·존재 확인·find 명령은 모두 정상 실행됐고(exit code 0), 스크립트 3개의 존재는
ls -la로 직접 확인했습니다." → 정상 실행과 exit code가 서술문 속에 있고 실제 명령 문자열이 없다. DB 사유: missing_verification_exit_code. 위 문장을 게이트 함수_codex_has_verification_result_field에 그대로 넣으면(False, 'missing_verification_result')가 나온다(4절). -
40842: 검증 줄 원문 "분류 합계를 확인했습니다. 4(...)+2(...)+...=8이고 ... exit code 0, 통과입니다." → 같은 구조. 재현 결과
(False, 'missing_verification_result'). DB 사유 3개(missing_execution_artifact, missing_verification_command, missing_verification_exit_code)가 한꺼번에 붙었다. -
40854: 결과 본문에 "missing_verification_exit_code: 4건 (guardian #40276, ...)" 형태의 라벨 집계가 있다. 게이트 정규식이
exit_code: 4건 (guardian #40276, #40346, #40633, #40842)를 검증 결과로 읽는다. 재현 결과(False, 'invalid_verification_exit_code'). 이 건은 guardian이 exit code를 빠뜨린 것이 아니라 라벨 집계 문자열이 게이트에 오탐된 것이다.
유형 B: 실패 원인 분류·크론 점검형 — "근거파일:"에 존재하지 않는 경로 인용
-
40949: DB 사유 "verification_command_unverifiable, execution_artifact_path_not_found:/Users/ron/.hermes/workspace/jobs.json, /Users/ron/.hermes/workspace/fi-morning-brief.sh". guardian이 파일명만 쓰면 게이트(
agent_queue_worker.py7123행add_path(WORKSPACE / raw))가 워크스페이스 기준 경로로 바꿔 존재 여부를 검사해 실패한다. -
40825 본문에서 guardian 스스로 원인을 적었다: "공통 원인은 guardian이 존재하지 않는
workspace/jobs.json을 인용하거나 재현 가능한 검증 명령을 적지 않은 것입니다. 실제 경로는/Users/ron/.hermes/cron/jobs.json입니다." 원인을 알고 있어도 다음 태스크 body에는 그 사실이 전달되지 않아 반복된다.
반복 구조
- 7일 archive의 주간 크레딧 점검 86건은 대부분 같은 문장 패턴의 재발행이다(예: #40600 body "대상: (1) /Users/ron/.hermes/cron/jobs.json 에서 job_id ..."에는 절대경로가 있었지만, 결과 인용 단계에서
jobs.json만 쓴 경우가 74건). - 오케스트레이터는 같은 실패를 보고도 body에 "이전 반려 사유와 금지 경로"를 넣지 않는다. 그 결과 CMD 번호만 바뀐 동일 태스크가 계속 발행된다.
4. [검증] 정규식 재현 (게이트 코드를 읽기 전용으로 실행, 수정 없음)
scripts/agent_queue_worker.py 7022~7040행 정규식과 7127~ _codex_has_verification_result_field를 그대로 exec로 불러 문자열 5종을 넣은 결과:
| 입력 검증 문구 | 결과 |
|---|---|
검증: 조회 결과 합계 일치 (exit code 0), 통과 |
(False, missing_verification_result) |
검증: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT count(*) FROM bus_commands" → 7행, exit code 0 |
(True) |
검증: 쿼리 재실행 + 다음 줄 검증 결과: 7행 반환, exit code: 0 |
(True) |
검증 결과: guardian 7건, failed 3건 확인, exit code: 0 |
(True) |
검증 결과: 3 failed 건 집계, exit code: 0 |
(False, invalid_verification_exit_code) — "N failed" 문구를 pytest 실패로 읽음 |
해석: guardian이 분류표를 쓸 때 "failed N건"을 "N failed" 어순으로 쓰면 반려된다. 화살표(→) 뒤에 exit code 0이 오거나 검증 결과: 라벨 뒤에 exit code: 0을 쓰면 통과한다.
이 5건은 폐쇄형 검증이다. 실제 운영 게이트에는 canonical 경로(public_result_formatter.py)가 따로 있고, 이 재현은 큐 저장용 폴백 함수에 대한 것이다. 두 경로의 결과가 다를 수 있다(예: #40869의 첫 note는 이 함수를 통과하지만 DB에는 invalid_verification_exit_code가 기록돼 있다). 이 불일치는 아직 원인을 확인하지 못했으므로 codex 조사 항목으로 남긴다.
5. 오케스트레이터 body 문구 개선안 (guardian 전용 추가 블록)
현재 _ensure_completion_evidence_block()(scripts/orchestrator.py 1083행~)의 자동보강 문구는 에이전트 구분이 없다. guardian일 때 아래 문장을 추가한다.
[guardian 완료보고 규칙]
1. 검증: 줄은 "검증: <재실행 가능한 전체 명령> → <결과값>, exit code: 0" 한 줄로 쓴다.
명령에 절대경로를 포함하고, "exit code: 0"을 콜론과 숫자까지 정확히 적는다.
서술문("정상 실행됐고")이나 "통과"만 쓰지 않는다.
2. "failed N건"처럼 쓰고 "N failed"는 쓰지 않는다. 라벨 집계에서 "missing_..._exit_code: N건" 형태를 검증 줄에 쓰지 않는다. 집계는 근거 줄이나 본문 표에 둔다.
3. 근거파일: 줄에는 ls로 존재를 확인한 절대경로만 쓴다.
크론 장부는 /Users/ron/.hermes/cron/jobs.json 이다. workspace/jobs.json, stdout.log, standard.md, config.yaml 같은 파일명만 쓰지 않는다.
4. 근거파일 줄은 "<절대경로>:<줄번호> \"<원문 1줄>\"" 형식이다. 게이트 자신의 반려 로그(completion_evidence_blocked.jsonl)는 인용하지 않는다.
5. 직전 반려 사유가 있는 재발행 태스크라면 그 라벨을 읽고 해당 항목부터 채운다.
재발행 태스크용 문구(오케스트레이터가 이전 실패 CMD를 알 때만 추가):
"직전 시도 #
6. guardian 완료보고 템플릿 예시 (분류표 작성형)
결론: bus_commands에서 guardian needs_evidence·failed 최근 7일 7행(live)을 조회해 사유 라벨별로 집계했다. exit code 계열이 5건(71.4%)으로 최다다.
근거: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT id,status,result_note FROM bus_commands WHERE claimed_by='guardian' AND status IN ('needs_evidence','failed') AND created_at>=datetime('now','localtime','-7 days')" → 7행, 조회 2026-10-06 04:19 KST
검증: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT count(*) FROM bus_commands WHERE claimed_by='guardian' AND status IN ('needs_evidence','failed') AND created_at>=datetime('now','localtime','-7 days')" → 7, exit code: 0
근거파일: /Users/ron/.hermes/data/ops_multiagent.db (bus_commands id=40854) "차단 사유: missing_verification_exit_code."
다음 행동: 추가 조치 없음
크론 점검형 예시:
결론: 주간 크레딧 요약 크론(job_id 5c45c26045be)의 enabled·lastStatus를 읽어 확인했다.
근거: /Users/ron/.hermes/cron/jobs.json에서 id가 5c45c26045be인 항목을 python3 json으로 읽음, 조회 YYYY-MM-DD HH:MM KST
검증: python3 -m json.tool /Users/ron/.hermes/cron/jobs.json → 출력 성공, exit code: 0
근거파일: /Users/ron/.hermes/cron/jobs.json:<줄번호> "<해당 항목 원문 1줄>"
다음 행동: <조치 또는 추가 조치 없음>
7. [리스크]
- 문구 개선만으로는 정규식 오탐이 사라지지 않는다. 라벨 집계 문자열(
..._exit_code: 4건)이 exit code로 읽히는 문제는 게이트 정규식 자체에서 고쳐야 한다. 이번 7일 live 7건 중 확인된 오탐은 #40854 1건(14.3%)이다. 나머지 exit code 계열은 guardian의 실제 서술 누락이다. - 선행 문서(
config/agent_completion_submission_standard.md§9-3-1, 570~590행)에 guardian 템플릿이 이미 있는데도 반려가 이어졌다. 같은 문서를 근거로 body에 넣지 않으면 효과가 없다는 뜻이다.completion_evidence_policy.mdv11절(1644~1720행)이 "문서 갱신 ≠ 프롬프트 배선"을 이미 경고했다. - 이 제안의
agent_registry.pyguardian 프롬프트(311~337행)는 이미 4블록 라벨을 안내한다. 그러나exit code: 0리터럴 형식과 금지 경로는 없다. - 계속된 재발행은 큐 용량도 소모한다. 7일 archive에서 guardian failed가 165건이다.
- 확인하지 못한 것: canonical 게이트 경로(
public_result_formatter.py)에서의 동일 재현, #40869의 불일치 원인.
8. [검증방법]
- KPI: 적용 후 72시간 guardian 반려(needs_evidence+failed) 건수. 기준: 7일 172건(일평균 약 24.6건). 목표: 적용 후 72시간 일평균 12건 이하(50% 감소). exit code 계열 라벨 live 5건 → 1건 이하.
- 측정 명령:
sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT count(*) FROM bus_commands WHERE claimed_by='guardian' AND status IN ('needs_evidence','failed') AND created_at>=datetime('now','localtime','-72 hours')"와 같은 조건의bus_commands_archive건수를 합산. 경로 라벨:grep -c 'completion-evidence-block' /Users/ron/.hermes/logs/agent-queue-guardian.log증가분과jobs.json인용 건수. - 롤백 조건: 적용 후 24시간 guardian 완료(done) 건수가 적용 전 24시간보다 줄거나, body 길이 증가로
llm_infra_failure라벨이 늘면 guardian 전용 블록을 제거한다.
9. codex 위임 후보 (코드 수정 대상 — 이 문서에서는 수정하지 않음)
scripts/orchestrator.py_ensure_completion_evidence_block()(1083행~): target agent가 guardian일 때 5절 규칙 블록을supplement에 추가. 호출 지점은 body 생성 경로(create_task()직전).scripts/agent_queue_worker.py7022~7040행_CODEX_VERIFICATION_RESULT_FIELD_RE의exit_code대안:[a-z_]*_exit_code[::=]\s*\d+건같은 라벨 집계 문자열 제외, 또는\b경계로missing_verification_exit_code같은 합성어 제외. 근거: #40854 재현. 함수_codex_has_verification_result_field(7127행~)의N failed오탐도\d+\s+failed정규식(_CODEX_PYTEST_FAILURE_RE)이 같이 점검 대상.scripts/agent_queue_worker.py7123행add_path(WORKSPACE / raw): 파일명만 인용된 경우 후보 루트에/Users/ron/.hermes/cron을 추가하거나, 실패 메시지에 "실제 경로 후보" 힌트를 붙이는 안(7268행 hint-only helper와 연결).scripts/agent_registry.pyguardian 프롬프트(311~337행): 6절 템플릿과 금지 경로 목록 추가.- 같은 제목 패턴으로 재발행되는 guardian 태스크(7일 archive 기준 주간 크레딧 점검형 86건)에 대한 오케스트레이터 중복 발행 억제는
hermes-orchestrator-circuit-breakers스킬 범위에서 별도 검토.