needs_evidence 반복 재발행 상위 3개 패턴 방지안 제안서
needs_evidence 반복 재발행 상위 3개 패턴 방지안 제안서
작성: Cowork(다듬) / 조회 2026-10-24 KST / 코드·DB·크론·설정 수정 없음 (sqlite3 -readonly, ps, grep, stat 조회만) 선행 문서: 261023_claude_needs_evidence_69건_상위원인_방지안.md (10-23 작성, 사유 라벨 기준). 이번 문서는 같은 71건을 "재발행 패턴(제목 계열)" 기준으로 다시 묶고, 원인을 지시문/제출 규율/게이트 오탐으로 가른다.
[권고]
1순위 (패턴 1, 51건): 집계·분류 태스크의 "건수만 바꿔 재발행" 고리를 오케스트레이터에서 끊는다. (원인 주체: 오케스트레이터 지시문, 보조: 게이트 오탐) → 무엇을: ① 제목에서 숫자를 뺀 계열 키가 같은 needs_evidence 태스크가 24시간 안에 2건 이상이면 본문 유사도와 무관하게 신규 발행을 보류한다. ② 집계 태스크 본문에 "분석 대상 라벨은 snake_case 코드명으로만 쓰고 '결과 확인 보류' 같은 한국어 차단 문구를 서술에 옮기지 않는다 + 검증 줄에 리터럴 'exit code 0'" 2줄을 고정 삽입한다. 실행은 codex 후속 태스크 F1·F2. → 왜: guardian 계열 44건은 연속 본문 유사도가 평균 0.30(최대 0.42, 261023 측정)인데 차단 임계는 0.9(orchestrator.py 4091행 REISSUE_LOOP_SIMILARITY)라 한 번도 걸리지 않았다. 10-04~10-24 중 20일에 걸쳐 발행됐다. → 기대효과: 신규 유입 15건/6일(id>42103, 전부 guardian) → 5건 이하/72시간.
2순위 (패턴 3, 9건): cowork "재발 방지안" 요청을 계열당 1개 "살아있는 문서"로 모으고, 새 방지안 요청 전에 기존 방지안(v1~v34, 본 문서 포함)과 대조하도록 지시문을 바꾼다. (원인 주체: 오케스트레이터 지시문 + cowork 제출 규율) → 왜: cowork 9건 전부가 "방지안/개선안/템플릿" 계열이며 10-06~10-24에 9번 재발행됐다. 사유는 invalid_verification_exit_code 3, 결과 확인 보류 3 등 제출 형식 문제다. 정책 문서는 이미 4697행·v34까지 있고, 같은 질문에 새 절이 계속 쌓인다. → 기대효과: cowork needs_evidence 신규 0~1건/주(현재 9건/19일 = 약 3.3건/주).
3순위 (패턴 2, 9건): 신규 조치 없음. 이미 막히지 않는 유형이므로 "실행 중 프로세스가 구버전인지" 확인하고 해리 승인 하에 재기동·잔여 9건 정리만 한다. (원인 주체: 증거 게이트 오탐, 현재는 이월 상태) → 왜: 9건(analyst-macro 5, analyst-pm 4) 전부 missing_result_or_verification, 마지막 발생 #41900(10-17) 이후 신규 0건. 261023 문서에서 현재 코드로 재판정하면 통과로 확인됨. → 기대효과: 잔여 9건 정리로 needs_evidence 71 → 62 (상태 변경은 해리 승인 후 별도).
[현황]
1. 읽은 자료와 확인 한계
- guardian 집계 태스크: CMD#42528 (claimed_by=guardian, status=done, completed_at 2026-10-24 10:39:06).
sqlite3 -readonly ... "SELECT id,claimed_by,status,... WHERE title LIKE '%needs_evidence 71건%'"→ 1행. - 중요: #42528의 DB result_note는 372자에서 끊겨 있고 근거파일 줄이 ":56 ..."으로 경로가 비어 있다. 저장 파일 경로는 result_note가 아니라 태스크 body의 예시 경로에서 확인했다 → /Users/ron/.hermes/workspace/memory/needs_evidence_breakdown_20261024.md. 파일을 읽어 제목 "needs_evidence 71건 에이전트별·사유별 집계", 조회 10:38 KST, 71행으로 태스크와 일치함을 확인했다(57줄). 즉 "guardian이 이 파일을 저장했다"는 점은 파일 내용 일치로 추정, result_note 자체로는 입증되지 않는다. (미확인)
- 현재 needs_evidence 총계:
SELECT status,COUNT(*) ...→ needs_evidence|71. id>42454(guardian 마지막 확인 이후) 신규는 1건(#42503, cowork).
2. 패턴별 건수 (직접 재조회)
| 패턴 | guardian 집계 | 내 재조회 | 에이전트 | id 범위·기간 |
|---|---|---|---|---|
| 1 집계·분류 | 48 | 51 | guardian+cowork | #40825~#42503, 10-04~10-24 |
| 2 IG·HY OAS 판독 | 9 | 9 | analyst-macro 5, analyst-pm 4 | #40408~#41900, 09-29~10-17 |
| 3 cowork 방지안 | 7 | 9 | cowork 9 | #40691~#42503, 10-04~10-24 |
- 패턴 1은 title LIKE '%needs_evidence%' AND (분류|집계|분포|반려) 조건으로 51건, guardian 기준과 3건 차이다. 3건 차이의 정확한 대상은 대조하지 않았다 (미확인). 패턴 1·3은 #42503 등 겹치는 건이 있어 합계가 71을 넘는다. |
||||
| - guardian 파일의 합계 대조(52+9+5+4+1=71)는 내가 다시 확인한 총계 71과 일치한다. | ||||
| - 최근 신규 유입 (id>42103, 17건): guardian 16 + cowork 1. 16건 중 제목이 집계·분류인 건이 거의 전부이며 간격은 3~12시간이다 (#42111, 42165, 42167, 42171, 42219, 42256, 42264, 42278, 42293, 42307, 42310, 42314, 42316, 42412, 42454). 이 중 #42332("#42127 실패 원인 분류")만 다른 성격의 단발 건이다. | ||||
| - 제목에 숫자가 54→55→56→57→58→60→61→62→63→64→65→68로 올라간다. 번호만 바뀐 같은 지시가 3~12시간마다 나간 것이다. |
3. 실행 중 코드와 파일 수정 시각 (10-23 문서의 사실을 10-24에 재확인)
- 프로세스 시작: orchestrator PID 46446 = 9/27 07:55, server PID 15335 = 9/28 09:17, agent_queue_worker 5종 PID 9095·9235·9282·9385·9540·9654 = 9/29 01:27, guardian worker PID 3974 = 9/29 02:16 (ps -axo pid,lstart).
- 파일 수정: agent_queue_worker.py 10/19 15:06, server.py 10/19 15:06, orchestrator.py 10/19 21:01, public_result_formatter.py 10/13 19:32 (stat -f '%Sm').
- 모두 프로세스가 파일보다 오래됐다 → 10/13 이후 수정은 실행 중 프로세스에 반영되지 않았을 가능성이 높다. (파이썬 모듈 로딩 시점 때문. 프로세스 내부 모듈 버전을 직접 보진 않았다 = 미확인.)
패턴별 원인 분류
패턴 1: 집계·분류 태스크 자기 재발행 (51건)
분류: ① 오케스트레이터 지시문 (주) / ② 증거 게이트 오탐 (보조) / ③ 에이전트 제출 규율 (일부)
- 오케스트레이터 지시문이 주원인인 근거: 라우팅 자체는 맞다(guardian은 DB 조회 담당). 문제는 같은 질문을 건수 숫자만 바꿔 20일에 걸쳐 재발행하는 것과, 재발행 차단기가 본문 유사도 0.9만 본다는 점이다. 실측 유사도 최대 0.42라 _is_needs_evidence_reissue_loop_tripped(orchestrator.py 4330행)는 구조적으로 작동하지 않는다. _reissue_family_key(4300행 부근)는 이미 제목 숫자를 제거해 계열 키를 만들고 있으므로 계열 키 기준 카운트를 쓰기만 하면 된다.
- 지시문 문구 문제: 태스크 body가 "근거 미기재로 반려됐으므로"(#42454)처럼 반려 문구를 그대로 서술시키고, 이를 받은 guardian이 "결과 확인 보류"를 보고서 본문에 옮겨 하드블록 정규식(server.py 687행 _COMPLETION_STORAGE_HARD_BLOCK_PATTERNS)에 걸린다. 반려 대상이 다음 집계 대상이 되는 자기참조다.
- 증거 게이트 오탐: 분석 보고서가 따옴표 없이 차단 문구를 서술하면 _completion_has_meta_investigation_report 예외를 못 받는 경우가 있다 (261023 재현: 6건 중 5건 차단 유지). 정규식이 "exit code는 0입니다"처럼 조사가 낀 표기를 못 읽는 것(agent_queue_worker.py 7120행 _CODEX_VERIFICATION_EXIT_CODE_RE)도 같은 계열이다.
- 제출 규율: #42454 body는 "exit code 0"까지 요구했는데도 result_note는 invalid_verification_exit_code로 반려됐다. 규율 문제도 일부 있다.
패턴 2: IG·HY OAS 판독 (9건)
분류: 증거 게이트 오탐 또는 이미 해소된 과거 상태(이월). 오케스트레이터 지시문 문제 아님, 에이전트 규율 문제 아님.
- 근거: 9건 전부 missing_result_or_verification. #41900 result_note의 "원본 요약"을 보면 결론에 IG OAS 84bp, HY OAS 325bp 등 실측 수치와 근거 파일 경로(/Users/ron/.hermes/workspace/memory/macro-timeseries/BAMLC0A0...)가 이미 있다. 에이전트는 증거를 냈는데 게이트가 "결과 확인 보류"로 바꿔 치운 형태다.
- 261023 재현: 원문 4건을 현재 파일 코드로 재판정하면 server 저장 게이트·워커 게이트 모두 통과. 당시 어느 게이트가 막았는지는 미확인.
- 신규 유입 0건(#41900 이후). 남은 일은 정리뿐이다.
패턴 3: cowork 재발 방지안 (9건)
분류: ① 오케스트레이터 지시문 (주) + ③ 에이전트 제출 규율 (부) - 지시문: 제목이 "N건 반려 패턴별 개선안 작성"(#41495), "N건 반복 사유별 방지안"(#41715), "N건 … 템플릿 개선안"(#41782)으로 거의 같다. 정책 문서에 이미 v1~v34가 쌓여 있는데(completion_evidence_policy.md 4697행) 요청마다 새 방지안을 새로 쓰게 하고, 산출물이 다시 같은 이유로 반려된다. cowork 프롬프트는 "300자 이상, 제안서 형식, 코드 작성 금지"에 4블록을 별도로 더 요구하는 이중 형식이라 충돌 여지가 있다. - 제출 규율: cowork 9건 사유는 invalid_verification_exit_code 3, 결과 확인 보류 3, 그 외 verification_command_unverifiable·completion_result_note_forces_rework·missing_* 각 1건. 비코딩 작업 완료 보고에서 "검증 줄에 exit code N 리터럴 + 실행 명령"을 안 지킨 경우다 (policy v31절 "비코딩 완료보고 4블록" 대상). - 증거 게이트 오탐 가능성: #42503의 원본 요약은 조회 시각·70행을 명시했는데 invalid_verification_exit_code만 지적받았다. 형식 요구가 지나치게 경직된 사례일 수 있으나 노트 전문을 확인하지 않아 확정하지 않는다 (미확인).
정책 대조 (v6절·AGENTS.md)
- completion_evidence_policy.md v6절 (398행~): 반려 사유를
missing_*(4블록 미비)와llm_infra_failure*(인프라)로 먼저 가르는 원칙. → 71건에서 인프라 사유로 반려된 건은 0건이다(10-24 집계 파일 3절 사유 표에 llm_infra_failure 없음). 따라서 전부 4블록·게이트 문제이고 "재시도 지연"으로 풀 문제가 아니다. - v6-1 정정 (477~497행): "코드로 이미 해소됐으니 코드 미반영이라고 단정하지 말라." → 패턴 2에 그대로 적용된다. 단 이번에는 조건이 하나 더 필요하다: 수정 시각이 아니라 프로세스 시작 시각이 수정 시각보다 늦은지까지 본다. 이번 실측에서 워커·서버·오케스트레이터가 전부 구버전 가능성이 있다. v6-1 말미에 "수정 시각 대비 프로세스 시작 시각 확인" 한 줄을 추가할 것을 권한다 (문서 반영은 별도 승인 후).
- AGENTS.md "대행 태스크 완료보고 검증 근거 정책" (64행~): 4블록 결론/근거/검증/근거파일을 순서·라벨 그대로 채우고 "정상 확인함" 단독 서술을 금지한다. → 패턴 1·3 body에는 이 4블록이 들어 있으나
[자동 보강]꼬리가 설명형 placeholder("수행한 일과 현재 상태를 완료형 한 문장으로 적는다")를 붙인다. 에이전트가 이를 답으로 베끼면 자기지시적 placeholder 금지 조항(AGENTS.md "금지" 항)에 걸린다. 이 보강 꼬리가 반려의 원인인지는 이번에 확정하지 못했다 (미확인). - AGENTS.md 115행·128행 라우팅 규칙: 코드 수정은 codex, run-cycle 지식 순환은 ron. → 이번 3패턴은 조회·분석류이므로 라우팅 오류가 아니다. 이 점에서 "라우팅 누락"으로 보는 가설은 기각한다.
- config/agent_completion_submission_standard.md 121행: "결과 확인 보류를 다시 결과로 제출하지 않는다" 1줄뿐이고, 분석 대상 라벨을 서술하는 방법은 규칙이 없다 → 규칙 공백. 후속 F3.
[리스크]
- 단일 장애점: 같은 정규식 계열이 server.py, agent_queue_worker.py, shared/public_result_formatter.py 세 곳에 사본으로 있다. 한 곳만 고치면 다시 갈라진다 (261023 문서 리스크와 동일).
- 구조적: needs_evidence에서 나가는 경로가 없다.
_cleanup_old_commands는 done/cancelled/failed만 정리하므로 71건이 누적되고, 집계 태스크가 그 건수를 다시 제목에 넣는다. - 계열 차단을 너무 넓히면 정당한 첫 집계도 막힌다. 24시간 내 2건 이상이라는 임계와 "직전 건이 done이면 통과" 조건을 둬야 한다.
- 하드블록 완화는 가짜 완료 통과 위험이 있다. "구체 근거 앵커 2개 이상 + 검증 줄에 실행 명령·exit code" 조건을 유지한다.
- 재기동은 진행 중 태스크를 끊는다. 해리 승인과 idle 확인이 필요하며 이 문서 작성자는 재기동하지 않았다.
- 내 집계(패턴 1: 51)와 guardian(48)의 3건 차이는 미해소다. KPI는 어느 한쪽 정의를 고정해서 쓴다.
[검증방법]
KPI (적용 후 72시간, 기준선은 id>42103 구간 17건):
1. 신규 유입: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT count(*) FROM bus_commands WHERE status='needs_evidence' AND id>(적용 시점 최대 id)" → 기대 5 이하.
2. 패턴 1 계열: 같은 쿼리에 AND title LIKE '%needs_evidence%' AND (title LIKE '%집계%' OR title LIKE '%분류%') → 기대 2 이하. 오케스트레이터 로그 grep -c NEEDS_EVIDENCE_REISSUE_LOOP_BLOCKED → 현재 0, 기대 1 이상(차단기 작동 증거).
3. 프로세스 반영: ps -o lstart= -p <orchestrator PID> 시각이 stat -f '%Sm' scripts/orchestrator.py 시각보다 늦을 것.
4. 합계 정합: 에이전트별 합 = 전체 건수를 매번 SQL로 대조 (현재 71).
5. 재현: 패턴 1 원문 6건 + 조사형 exit code 8건을 server._completion_storage_block_reasons로 재판정 → 14건 중 12건 이상 [].
롤백: 가짜 완료(경로·명령이 허구)가 done으로 저장된 사례가 1건이라도 나오면 완화를 즉시 되돌린다. 재기동 후 워커 기동 실패 시 이전 설정으로 복구한다. 정상 첫 집계 태스크가 계열 차단으로 막히는 사례가 2건 나오면 임계를 2→3으로 올린다.
후속 codex 태스크 후보 (직접 작성하지 않음)
- F1 수정 대상: scripts/orchestrator.py
_is_needs_evidence_reissue_loop_tripped(4330행),REISSUE_LOOP_SIMILARITY(4091행),_reissue_family_key. 내용: 계열 키가 같은 needs_evidence 건이 24시간 내 2건 이상이면 유사도와 무관하게 신규 발행 보류, 직전 건이 done이면 통과. 검증 명령:python3 -m pytest tests -k reissue -q→ 신규 케이스(계열 키 동일·유사도 0.3) 통과, exit code 0. - F2 수정 대상: scripts/orchestrator.py 집계류 태스크 body 생성부와
[자동 보강]꼬리 생성부(1146행 부근 supplement). 내용: 설명형 placeholder 대신 "분석 대상 라벨은 snake_case, 검증에 리터럴 exit code 0"만 삽입. 검증 명령:python3 -m py_compile scripts/orchestrator.py→ exit code 0, 그리고 생성된 body에서grep -c "수행한 일과 현재 상태를 완료형"→ 0. - F3 수정 대상: config/agent_completion_submission_standard.md 121행 부근 §3-1. 내용: 분석 대상 라벨 서술 규칙 1줄. 검증 명령:
grep -n "snake_case" config/agent_completion_submission_standard.md→ 1건 이상. - F4 수정 대상: scripts/agent_queue_worker.py
_CODEX_VERIFICATION_EXIT_CODE_RE(7120행), scripts/server.py_STORAGE_EXIT_CODE_RE(733행). 내용: "exit code는 0입니다" 조사 허용을 두 사본에 동시 적용. 검증 명령: 조사형 원문 8건 재판정 → 8건 []. - 기록 후보: completion_evidence_policy.md v6-1 끝에 "프로세스 시작 시각 ≥ 파일 수정 시각 확인" 1줄 (문서 수정, 해리 승인 후).
- 상태 정리 후보: 패턴 2의 잔여 9건 처리는 해리 승인 후 별도 진행. 이 문서에서는 상태 변경·삭제를 하지 않았다.