virtual-insanity
← 리포트 목록

needs_evidence 58건 반려 상위 3유형 완료보고 개선안

2026-10-20 hermes [needs_evidence, completion-gate, 4block, proposal]

needs_evidence 58건 반려 상위 3유형 — 완료보고 개선안

작성: cowork(다듬) / 조회 2026-10-20 13:38 KST / 코드·DB·크론 수정 없음(제안서만)

1. [권고]

1순위 — "가짜 4블록 템플릿 복사"와 "메타 태스크 자기참조"를 먼저 끊는다 (유형 A, 21건 36%). - 무엇을: codex가 scripts/orchestrator.py의 [자동 보강: 완료보고 4블록 설명형 라벨 보충] 문구(1146행 부근 supplement, 1560행 marker)를 에이전트가 값으로 베끼지 못하게 바꾼다. 지금은 "결론: 수행한 일과 현재 상태를 완료형 한 문장으로 적는다." 같은 지시문이 그대로 태스크 본문에 붙어 에이전트가 그 문장을 결론 값으로 복사한다(CMD#40276 실물 확인). - 왜: 58건 중 43건(74%)이 제목에 needs_evidence/반려가 들어간 "반려 분류" 메타 태스크다. 이 태스크들은 본문에 반려 노트 원문(라벨 문자열 missing_*, "결과 확인 보류")을 인용하게 되어, 인용문이 곧 게이트 입력이 되어 재반려된다. 유형 A 21건은 전부 제목이 메타 태스크다(비메타 0건). - 기대효과: 유형 A 21건 + 유형 B 중 메타 태스크분이 사라지면 58건 중 최대 약 39건(67%) 감소 가능(A 21 + B 18, 일부 중복 가능성 있어 상한치).

2순위 — exit code 리터럴 한 줄 규격을 태스크 본문 템플릿에 못박는다 (유형 B, 18건 31%). - 무엇을: 검증 줄을 검증: <명령> → <결과>, exit code 0 형태로만 받도록 지시문 템플릿과 TL;DR(§0 5줄 체크)에 "exit code 숫자는 같은 줄, 대시/콜론 구분 없이 exit code 0" 예시를 추가한다. 게이트는 exit code/rc 뒤 숫자가 있어야 통과(아래 검증 코드 7219~7247행). - 왜: 유형 B 18건 중 15건이 guardian이고, 저장된 발췌에서 exit code N 리터럴이 확인된 것은 1/18뿐이다. - 기대효과: guardian 단독 누락분 15건(26%) 해소 목표.

3순위 — analyst-macro/analyst-pm의 수치 보고에 검증 줄을 붙이는 전용 템플릿 (유형 C, 9건 16%). - 9건 모두 "결론:"은 있고 "근거:" 뒤가 비거나(CMD#40408) 검증 줄이 없다.

2. [현황]

2-1. 집계 (sqlite3 직접 조회 + guardian 집계 JSON 대조)

  • 전체 58건. 에이전트별: guardian 40(69%), cowork 8(14%), analyst-macro 5(9%), analyst-pm 4(7%), ron 1(2%). guardian 집계 JSON(/Users/ron/.hermes/workspace/memory/needs_evidence_triage_20261020.json, 2026-10-20 12:35 저장)의 by_agent와 일치.
  • 방침: 같은 날 guardian JSON이 있어 라벨 분포는 그 값을 기준으로 삼고, 아래 "차단 사유 첫 라벨" 기준 집계(대표 라벨 1개로 건당 1회 카운트)는 내가 result_note에서 직접 다시 뽑아 교차 확인용으로만 썼다. guardian JSON은 건당 복수 라벨을 중복 카운트하므로(라벨 합 > 58) 숫자가 다르다.

guardian JSON(복수 라벨 포함, 건수 합 > 58): 라벨없음 18, missing_verification_exit_code 17, missing_result_or_verification 13, llm_infra_failure 11, missing_verification_command 5, missing_execution_artifact 3, execution_artifact_path_not_found 2, 기타 3.

직접 재집계(첫 차단 사유 기준, 합 = 58):

유형 건수 비율 에이전트
A. 라벨 없는 "결과 확인 보류" 21 36% guardian 18, cowork 3
B. exit code 계열(missing 10 + invalid 8) 18 31% guardian 15, cowork 2, ron 1
C. missing_result_or_verification 9 16% analyst-macro 5, analyst-pm 4
기타 10 17% missing_execution_artifact 3, completion_result_note_forces_rework 3, verification_command_unverifiable 2, execution_artifact_path_not_found 2

차이 해설: guardian JSON은 A=18/B=17(missing만)/C=13, 내 재집계는 A=21/B=18/C=9다. 원인은 건당 복수 라벨(예: CMD#42042는 5라벨) 처리 방식 차이다. 상위 3유형 순위(A > B > C)는 두 집계에서 같다.

LLM 인프라 실패: 노트 본문 어딘가에 llm_infra_failure가 언급된 건이 14건이지만 "차단 사유" 칸에 올라간 건은 0건이다. 즉 인프라 실패는 반려의 원인이 아니라 분류 태스크 본문의 인용 단어다(예: CMD#40825 "인프라 실패(llm_infra_failure)는 0건").

2-2. 핵심 구조 발견: 저장된 result_note는 에이전트 원문이 아니다

모든 needs_evidence 노트가 결론: 결과 확인 보류 / 근거: 결과물과 검증 근거가 없음. 차단 사유: ... 원본 요약: <220자 발췌> / 다음 행동: 위 보완 항목을 ... 고정 3줄이다. 길이는 최대 486자, 평균 387자. - 출처: scripts/shared/public_result_formatter.py 3981행 guarded_completion_result_note()가 게이트 실패 시 이 템플릿으로 덮어쓰고, 3933행 _completion_raw_excerpt(text, max_len=220)가 원문을 220자로 자른다. - 따라서 "에이전트 원문 4블록이 어디서 어긋났나"는 DB의 result_note만으로는 끝까지 확인할 수 없다. 아래 인용은 발췌 220자까지 보이는 범위 내의 사실만 쓴다.

3. 상위 3유형별 4블록 표준과의 어긋남

기준(config/agent_completion_submission_standard.md 107~116행): 결론: / 근거:(경로+조회시각) / 검증:(명령 → 결과값 또는 exit code) / 근거파일:(경로:줄번호 "원문") / 다음 행동:.

유형 A. 라벨 없는 "결과 확인 보류" — 21건 (guardian 18, cowork 3)

어긋남: 차단 사유 칸에 구체 라벨이 없고 "결과 확인 보류" 자체가 사유로 적혀 있다. 즉 게이트가 라벨을 못 만든 채 닫았다. 발췌를 보면 결론·근거 문장은 있으나 검증: 줄, 근거파일: 줄이 발췌 범위 안에 나타나지 않는다. 원문이 서술형(결론: ... 근거: ... 한 문단)으로 이어져 줄 단위 라벨이 분리되지 않는 것이 공통점이다.

원문 인용 1 — CMD#41101 (guardian, "최근 24시간 needs_evidence·failed 태스크 원인 분류 (SQL 집계)"):

원본 요약: 결론: 최근 24시간 needs_evidence·failed 태스크 7건과 현재 큐 전체 23건(needs_evidence 18, failed 5)을 3종으로 분류했다. 코드는 수정하지 않았다. 근거: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db로 bus_commands를 조회했다(2026-10-07 14:18 KST). 24...

→ 조회 명령·시각은 있으나 줄 단위 검증:이 발췌 안에 없고, 근거:가 같은 문단에 인라인으로 붙어 있다.

원문 인용 2 — CMD#41495 (cowork, "needs_evidence 34건 반려 패턴별 완료보고 개선안 작성"):

원본 요약: 결론: needs_evidence 34건을 직접 조회해 에이전트별·사유별로 집계하고, 상위 2개 사유의 원인을 구분했습니다. 기존 두 정책 문서와 대조해 신규 항목만 제안서(memory/needs_evidence_34_proposal_20261012.md)로 정리했습니다. 근거: `sqlite3 -readonly ...\"S

→ 산출물 경로는 있으나 근거파일:에 줄번호+원문 인용이 없고(경로만), 검증:이 없다.

유형 B. exit code 계열 — 18건 (guardian 15, cowork 2, ron 1)

어긋남: 검증: <명령> → <결과> 줄에 exit code N 리터럴이 없거나(missing 10건), exit code가 0이 아니거나 pytest 실패 숫자가 섞였다(invalid 8건). 게이트 _codex_has_verification_result_field()(scripts/agent_queue_worker.py 7219행)는 7242행에서 exit code 정규식 매치가 없으면 missing_verification_exit_code, 7235·7245·7247행에서 값이 비정상이면 invalid_verification_exit_code를 반환한다. 표준 §3의 검증 예시는 "→ 12 passed"처럼 exit code를 안 적어도 되는 인상을 주는데, 이 게이트는 "passed" 화살표 형식 외에는 exit code 숫자를 요구한다.

원문 인용 1 — CMD#40276 (guardian, "agent_queue_worker.py 모델 타임아웃 패치가 실행 중인 워커에 반영됐는지 확인", 차단 사유 missing_verification_exit_code):

원본 요약: 결론: 수행한 일과 현재 상태를 완료형 한 문장으로 적는다. 근거: /Users/ron/.hermes/workspace/scripts/agent_queue_worker.py, /Users/ron/.hermes/workspace/tests/test_agent_queue_worker_model_timeout.py, scripts/agent_queue_worker.py, tests/test_age...

→ 결론 값이 실제 결과가 아니라 "[자동 보강] 설명형 라벨" 지시문 그 자체다. 에이전트가 템플릿 설명문을 값으로 복사했다. 근거 칸은 경로만 나열(조회시각 없음).

원문 인용 2 — CMD#41348 (cowork, "needs_evidence 25건 반려 라벨별 재발 방지안 정리", 차단 사유 invalid_verification_exit_code):

원본 요약: 결론: 12:08에 조회한 반려 29건을 라벨별·에이전트별로 집계했습니다. 상위 2개 라벨의 4블록 누락 여부를 로그 원문에서 확인했고, 재발 방지 제안서를 작성했습니다. 근거: 실행한 sqlite3 명령은 `sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db \"SELECT id,claimed_by,status,result_note FR...

→ 제목은 25건인데 본문은 29건(제목-본문 건수 불일치)이고, 조회시각이 날짜 없이 "12:08"뿐이다. 반려 사유가 invalid이므로 검증 줄 값에 라벨 문자열(예: missing_verification_exit_code 8건의 숫자)이나 실패 건수가 섞여 exit code로 오인됐을 가능성이 있다(2026-10-13 CMD#41573 패치가 정규식 왼쪽 경계를 추가한 이력이 있으나 이 건은 그 이전 노트인지 미확인).

유형 C. missing_result_or_verification — 9건 (analyst-macro 5, analyst-pm 4)

어긋남: 결론 문장(수치 포함)은 있지만 근거: 뒤가 비었거나 검증 줄이 없다. 해당 두 에이전트는 코드/문서 작업이 아닌 파일 읽기 기반 수치 판독이라 "검증 명령"이라는 개념이 템플릿에 없다. 9건 모두 조회 대상은 memory/macro-timeseries/*.json 같은 파일 기반 판독이다.

원문 인용 1 — CMD#40408 (analyst-macro, "미국 IG 등급별 OAS 격차 산출 (us-ig-weekly-latest.json 직접 파싱)"):

원본 요약: 결론: us-ig-weekly-latest.json을 직접 파싱해 AAA/AA/A/BBB OAS와 BBB-AA(39bp), BBB-A(30bp), A-AAA(27bp) 격차 및 1주·1개월 변화를 계산했다. ... 근거:

→ 근거: 라벨 뒤가 비어 있다(발췌가 근거:에서 끝남). 파일 경로·조회시각·검증이 없다.

원문 인용 2 — CMD#40518 (analyst-macro, "미국 IG 회사채 OAS 최근 추이 정리"):

원본 요약: 결론: 미국 IG OAS 추이 정리를 끝내고 md 파일로 저장했습니다. OAS는 83bp(2026-09-28 기준)로, ... 근거: memory/macro-timeseries/BAMLC0A0CM.json을 직접 파싱했고 외부 호출은 없습니다. 조회 시각은 2026

→ 근거 경로는 있으나 저장했다는 md 파일의 경로가 없고, 검증(검증: ... → exit code 0)과 근거파일:(줄번호+원문 인용)이 없다.

보조 확인: 같은 유형의 analyst-pm CMD#40874도 "결론"에 해석(격차 확대 +37bp)이 있으나 근거 파일 경로·검증 줄이 발췌에 없다.

4. 코드 수정 제안 (codex 위임 후보 — 나는 수정하지 않음)

# 대상 파일 함수/위치 변경 방향 검증 명령 후보
1 scripts/orchestrator.py 1146행 부근 supplement 생성부, 1560행 marker 설명형 라벨 보강 문구가 결론: <값> 형태가 아니라 <…> 자리표시자 + "이 문장을 복사하지 말 것" 명시. 또는 보강 블록을 본문 끝이 아닌 별도 필드로 grep -n "수행한 일과 현재 상태" scripts/orchestrator.py 후 python3 -m pytest tests/ -k "supplement or evidence" -q → N passed, exit code 0
2 scripts/shared/public_result_formatter.py completion_required_evidence_gaps(3134행) 입력 전처리 result_note 결론 값이 자동보강 설명문 리터럴("완료형 한 문장으로 적는다")과 일치하면 completion_has_self_referential_placeholder_evidence()처럼 즉시 차단 라벨(예: placeholder_copied_from_template) 부여 → 유형 A/B의 "라벨없음" 감소 신규 pytest 1건 + python3 -m pytest tests/test_formatter_evidence_placeholder_39871.py -q
3 scripts/shared/public_result_formatter.py guarded_completion_result_note(3981행), _completion_raw_excerpt(3933행, max_len=220) 발췌 220자 상한 때문에 원인 분석이 불가하다. 차단 사유가 결과 확인 보류 단독일 때 completion_required_evidence_gaps 결과 라벨을 강제로 병기하고, 원문 전체를 logs/ 별도 파일 경로로 남겨 노트에 경로 1개 포함 합성 입력으로 함수 호출 후 차단 사유:에 구체 라벨이 있는지 assert, exit code 0
4 scripts/agent_queue_worker.py _codex_has_verification_result_field(7219행), 정규식 _CODEX_VERIFICATION_EXIT_CODE_RE(public_result_formatter.py 3295행 부근 _CODEX_*) 반려 라벨이 missing_verification_exit_code일 때 "보완 필요" 문구에 정확한 예시 1줄(검증: <명령> → <결과>, exit code 0)을 넣는다. _completion_retry_items(4172행 매핑표)의 "검증 결과의 exit code" 문구 확장 python3 -m pytest tests/test_completion_gate_needs_evidence_41573.py -q
5 scripts/orchestrator.py 태스크 생성부 제목에 needs_evidence/반려를 포함하는 분류 태스크 생성 동일 분류 태스크 연속 재발행 차단(최근 12~24시간 내 같은 패턴 제목 존재 시 신규 발행 억제). 43/58건이 이 패턴 sqlite3 -readonly ... "SELECT COUNT(*) FROM bus_commands WHERE status='needs_evidence' AND (title LIKE '%needs_evidence%' OR title LIKE '%반려%')" → 43 이하로 감소 추세
6 analyst-macro/analyst-pm 역할 프롬프트 scripts/agent_registry.py AGENT_ROLE_PROMPTS (해당 항목) 파일 판독형 보고용 4블록 예시 추가(근거: 파일 경로+조회시각, 검증: python3 -c 'json.load(...)' → 값, exit code 0) python3 -m py_compile scripts/agent_registry.py → exit code 0

라우팅: 위 6건은 소스 파일 수정이므로 codex 전담(2026-09-21 규칙). cowork로는 보내지 않는다.

5. [리스크]

  • 단일 장애점: 게이트가 agent_queue_worker.py와 public_result_formatter.py 두 곳에 사본이 있다(표준 문서 §1 40~44행). 한쪽만 패치하면 "고쳤는데 계속 막힘"이 재발한다. 제안 #2·#4는 양쪽 동시 반영이 필수다.
  • 순환 위험: 반려 분류 태스크가 반려되면 새 분류 태스크가 발행되어 건수가 올라간다. 오늘만 해도 제목 숫자가 52→58로 증가했다(CMD#42062 52건 → 본 건 58건). 발행 억제(#5)가 없으면 개선안 반영 후에도 분류 태스크 자체가 계속 needs_evidence로 쌓인다.
  • 증거 손실: 220자 발췌(#3) 때문에 사후 원인 분석이 어렵다. 이번 유형별 판정도 발췌 범위 안의 근거에 한정된다.
  • 한계(미확인): 유형 B invalid 8건의 개별 원인(exit code 비0 vs 식별자 숫자 오인)은 원문이 없어 확정하지 못했다. 2026-10-13 패치 이전/이후 노트인지는 completed_at 대조가 필요하다.

6. [검증방법]

성공 기준(패치 반영 후 24시간): - needs_evidence 총건수: 현재 58 → 30 이하. - 유형 A(차단 사유가 "결과 확인 보류" 단독) 21 → 5 이하. - 유형 B 18 → 6 이하(guardian 15 → 5 이하). - 측정 명령: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT claimed_by,COUNT(*) FROM bus_commands WHERE status='needs_evidence' GROUP BY 1" - 롤백 조건: 패치 후 정상 done 완료가 오히려 needs_evidence로 바뀌는 비율이 증가(예: 24시간 내 새 needs_evidence 중 비분류 태스크가 5건 초과), 또는 python3 -m pytest tests/ -k evidence -q가 실패하면 해당 패치를 되돌린다.

부록. 재현 명령

  • sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT COUNT(*) FROM bus_commands WHERE status='needs_evidence'" → 58 (2026-10-20 13:38 KST)
  • 재집계 스크립트: /Users/ron/.hermes/workspace/tmp/ne58_agg.py, /Users/ron/.hermes/workspace/tmp/ne58_chk.py (읽기 전용 sqlite 연결)
  • 참조 JSON: /Users/ron/.hermes/workspace/memory/needs_evidence_triage_20261020.json