multi-agent-mux-loop 가 codex 리뷰어에게 리뷰 job 을 보낼 때, 프롬프트가 실제로는 전달되었는데도send_keys_safe 가 전달 실패로 판정합니다. job 은 즉시 error (agent_blocked) 로 종결되고, codex 가 이후 발행하는 started/completed 이벤트는 terminal 상태라 CAS 에서 거부됩니다. 결과적으로 codex 의 판정(PASS/NOT PASS)은 루프에 한 번도 반영되지 않으며, codex 가 포함된 --all-reviewer 루프는 만장일치에 도달할 수 없습니다.
늦게 도착한 completed 를 거부하는 것 자체는 Issue #6 의 fail-closed 설계이므로 결함이 아닙니다. 결함은 전달 성공을 실패로 오판하는 부분입니다.
재현 (Reproduction) — 3/3
run_loop.sh --creator creator-claude-01 --all-reviewer --max-loop 3 (리뷰어: claude·agy·grok·codex) 실행 중 codex 리뷰 job 이 매 이터레이션마다 동일하게 실패:
job
error (agent_blocked)
codex started
codex completed
레코드 최종 status
98093686 (iter 1)
11:47:41Z
11:47:43Z (+2s)
11:50:23Z — PASS
error
120ace35 (iter 2)
12:11:07Z
12:11:10Z (+3s)
12:13:41Z — NOT PASS
error
a536fce2 (iter 3)
12:19:39Z
12:19:39Z (+0s)
(검토 진행 중에 루프 종료)
error
루프 로그: Monitoring job '<id>' for status changes (timeout: 5.0s)... Reviewer 'planner-reviewer-codex-01' job crashed.
실패 시 2431행 sleep 2 후 herdr agent get 의 agent_status 가 working 인지 1회 확인 (2442행)
아니면 2445행 delivery unknown/failed 출력 후 return 4 → 호출 측에서 job 을 error agent_blocked 로 종결
추정 원인: 프롬프트 전송 후 약 7초(5s wait + 2s) 이내에 herdr 가 codex 의 agent_status 를 working 으로 보고하지 않음. codex 는 프롬프트를 정상 수신해 0–3초 내 started 를 발행했으므로, 전달 확인 신호(herdr agent_status)와 실제 전달 사이의 불일치로 보임 (codex 상태 전이 지연/미검출 여부는 미측정)
단일 주입 계약(2429행 주석: 재전송 금지)상 재시도도 하지 않으므로, 오판이 그대로 job 종결로 이어짐
영향 (Impact) — High
codex 가 리뷰어로 포함된 모든 --all-reviewer / --reviewer 루프에서 codex 판정이 무시되고 crashed 로 처리 → 만장일치 불가, --max-loop 소진 후 실패
codex 가 실제로 수행한 리뷰(수 분 소요)가 낭비되고, 리포트는 남지만 레지스트리 상태와 불일치
제안 해결책 (Proposed Fix)
전달 확인을 herdr agent_status == working 단일 신호에 의존하지 말고, 해당 job 의 started 이벤트 수신(또는 pane 에 프롬프트 에코 확인)도 성공 신호로 인정
return 4(delivery unknown) 는 즉시 error 로 종결하지 말고 "unknown" 상태로 두고 started 이벤트를 짧은 유예 시간 동안 기다린 뒤 판정 (단일 주입 계약은 유지 — 재전송 없음)
codex 어댑터의 --until working 타임아웃(5000ms)이 충분한지 측정 후 에이전트별 설정값으로 분리
회귀 테스트: agent prompt 가 timeout 을 반환하지만 started 가 도착하는 케이스에서 job 이 error 로 종결되지 않아야 함
비교: agy 의 42dadf59 도 agent_blocked 였으나 이후 started 가 없고 pane 이 이전 작업 후 "Generating..." 에 멈춘 상태여서 본 이슈와 별개로 판단함
## 문제 개요 (Summary)
`multi-agent-mux-loop` 가 codex 리뷰어에게 리뷰 job 을 보낼 때, 프롬프트가 **실제로는 전달되었는데도** `send_keys_safe` 가 전달 실패로 판정합니다. job 은 즉시 `error` (`agent_blocked`) 로 종결되고, codex 가 이후 발행하는 `started`/`completed` 이벤트는 terminal 상태라 CAS 에서 거부됩니다. 결과적으로 codex 의 판정(PASS/NOT PASS)은 루프에 **한 번도 반영되지 않으며**, codex 가 포함된 `--all-reviewer` 루프는 만장일치에 도달할 수 없습니다.
> 늦게 도착한 `completed` 를 거부하는 것 자체는 Issue #6 의 fail-closed 설계이므로 결함이 아닙니다. 결함은 **전달 성공을 실패로 오판하는 부분**입니다.
---
## 재현 (Reproduction) — 3/3
`run_loop.sh --creator creator-claude-01 --all-reviewer --max-loop 3` (리뷰어: claude·agy·grok·codex) 실행 중 codex 리뷰 job 이 매 이터레이션마다 동일하게 실패:
| job | error (agent_blocked) | codex started | codex completed | 레코드 최종 status |
|---|---|---|---|---|
| `98093686` (iter 1) | 11:47:41Z | 11:47:43Z (+2s) | 11:50:23Z — PASS | `error` |
| `120ace35` (iter 2) | 12:11:07Z | 12:11:10Z (+3s) | 12:13:41Z — NOT PASS | `error` |
| `a536fce2` (iter 3) | 12:19:39Z | 12:19:39Z (+0s) | (검토 진행 중에 루프 종료) | `error` |
- 루프 로그: `Monitoring job '<id>' for status changes (timeout: 5.0s)... Reviewer 'planner-reviewer-codex-01' job crashed.`
- submit 로그: `send_keys_safe: delivery unknown/failed (planner-reviewer-codex-01)` / `ERROR: 프롬프트 주입 실패 — 세션 'planner-reviewer-codex-01' (프롬프트 잠금 의심)`
- codex pane 에도 `Completion event rejected because the job was already terminal error.` 가 출력됨
- 같은 루프의 claude·agy·grok 리뷰어는 정상 (iter 3 에서 3인 PASS). 루프는 codex 한 명 때문에 `exit 1` 로 종료
---
## 세부 원인 분석 (Detailed Root Causes)
- **위치**: `.agents/skills/lib.sh` `send_keys_safe` (2330행~), 특히 2400–2446행
- **동작**:
- 2400행: `herdr agent prompt <target> <text> --wait --until working --timeout 5000`
- 실패 시 2431행 `sleep 2` 후 `herdr agent get` 의 `agent_status` 가 `working` 인지 1회 확인 (2442행)
- 아니면 2445행 `delivery unknown/failed` 출력 후 `return 4` → 호출 측에서 job 을 `error agent_blocked` 로 종결
- **추정 원인**: 프롬프트 전송 후 약 7초(5s wait + 2s) 이내에 herdr 가 codex 의 `agent_status` 를 `working` 으로 보고하지 않음. codex 는 프롬프트를 정상 수신해 0–3초 내 `started` 를 발행했으므로, **전달 확인 신호(herdr agent_status)와 실제 전달 사이의 불일치**로 보임 (codex 상태 전이 지연/미검출 여부는 미측정)
- 단일 주입 계약(2429행 주석: 재전송 금지)상 재시도도 하지 않으므로, 오판이 그대로 job 종결로 이어짐
---
## 영향 (Impact) — High
- codex 가 리뷰어로 포함된 모든 `--all-reviewer` / `--reviewer` 루프에서 codex 판정이 무시되고 `crashed` 로 처리 → 만장일치 불가, `--max-loop` 소진 후 실패
- codex 가 실제로 수행한 리뷰(수 분 소요)가 낭비되고, 리포트는 남지만 레지스트리 상태와 불일치
---
## 제안 해결책 (Proposed Fix)
- 전달 확인을 herdr `agent_status == working` 단일 신호에 의존하지 말고, 해당 job 의 `started` 이벤트 수신(또는 pane 에 프롬프트 에코 확인)도 성공 신호로 인정
- `return 4`(delivery unknown) 는 즉시 `error` 로 종결하지 말고 "unknown" 상태로 두고 `started` 이벤트를 짧은 유예 시간 동안 기다린 뒤 판정 (단일 주입 계약은 유지 — 재전송 없음)
- codex 어댑터의 `--until working` 타임아웃(5000ms)이 충분한지 측정 후 에이전트별 설정값으로 분리
- 회귀 테스트: `agent prompt` 가 timeout 을 반환하지만 `started` 가 도착하는 케이스에서 job 이 `error` 로 종결되지 않아야 함
---
## 환경 정보 (Environment)
- OS: macOS (Darwin 25.6.0)
- MAM Version: 4d444ce (main)
- Agent: codex (`planner-reviewer-codex-01`, gpt-6-astra high), herdr session `multi-agent-mux`
- 비교: agy 의 `42dadf59` 도 `agent_blocked` 였으나 이후 `started` 가 없고 pane 이 이전 작업 후 "Generating..." 에 멈춘 상태여서 **본 이슈와 별개**로 판단함
원인 실측: herdr 0.9.0 은 codex 에 프롬프트를 정상 전달한 뒤 agent prompt --wait --until working --timeout 5000 에 {"code":"timeout"} 을 돌려주고, codex 의 agent_status 는 작업 중에도 계속 idle 로 보고됨.
수정 내용
send_keys_safe: timeout/agent_prompt_stalled(= 입력은 전달됐고 상태 관측만 실패)이면 SKS_DELIVERY_GRACE_S(기본 15초) 동안 working 상태, 제출 전엔 없던 pane busy 토큰(esc to interrupt), 제출 전 스냅샷 대비 pane 변화 중 하나를 전달 확인 신호로 인정. 신호가 없으면 종전대로 rc 4 fail-closed, transport_error 등은 종전 의미 유지, 단일 주입 계약 유지 (497633f)
래퍼 _inject_acknowledged: 전달 미확인 시 MAM_INJECT_ACK_GRACE_S(기본 10초) 동안 레지스트리를 관측해 pending 을 벗어났으면(에이전트의 started) agent_blocked 로 닫지 않고 진행 (497633f)
래퍼 R1 유예 _agent_still_working: herdr working 외에 pane busy 토큰 인정 (eaa49aa); _recent_disk_heartbeat: events.log 의 최근 heartbeat 도 생존 근거로 인정하고 근거마다 idle 창을 재개방, 벽시계 START_TIME+TIMEOUT 에서 확정 종료 (d5a191c, 952657e)
publish_event: 이벤트를 브로커 발행 전에 로컬 events.log 에 기록 (ebe58f6)
검증
tests/test_issue14_delivery_ack.py 등 신규 테스트, 전체 스위트 819/819 통과
실측: codex 위임 job d04ebcb5 의 started/completed 가 agent_blocked 없이 수락; codex 포함 --all-reviewer 루프가 만장일치 PASS 로 종료
브랜치 `fix/issue-9-14` 에서 해결 (main `4d444ce` 기준, 병합 전).
**원인 실측**: herdr 0.9.0 은 codex 에 프롬프트를 정상 전달한 뒤 `agent prompt --wait --until working --timeout 5000` 에 `{"code":"timeout"}` 을 돌려주고, codex 의 `agent_status` 는 작업 중에도 계속 `idle` 로 보고됨.
**수정 내용**
- `send_keys_safe`: `timeout`/`agent_prompt_stalled`(= 입력은 전달됐고 상태 관측만 실패)이면 `SKS_DELIVERY_GRACE_S`(기본 15초) 동안 `working` 상태, 제출 전엔 없던 pane busy 토큰(`esc to interrupt`), 제출 전 스냅샷 대비 pane 변화 중 하나를 전달 확인 신호로 인정. 신호가 없으면 종전대로 rc 4 fail-closed, `transport_error` 등은 종전 의미 유지, 단일 주입 계약 유지 (`497633f`)
- 래퍼 `_inject_acknowledged`: 전달 미확인 시 `MAM_INJECT_ACK_GRACE_S`(기본 10초) 동안 레지스트리를 관측해 `pending` 을 벗어났으면(에이전트의 `started`) `agent_blocked` 로 닫지 않고 진행 (`497633f`)
- 래퍼 R1 유예 `_agent_still_working`: herdr `working` 외에 pane busy 토큰 인정 (`eaa49aa`); `_recent_disk_heartbeat`: `events.log` 의 최근 heartbeat 도 생존 근거로 인정하고 근거마다 idle 창을 재개방, 벽시계 `START_TIME+TIMEOUT` 에서 확정 종료 (`d5a191c`, `952657e`)
- `publish_event`: 이벤트를 브로커 발행 **전에** 로컬 `events.log` 에 기록 (`ebe58f6`)
**검증**
- `tests/test_issue14_delivery_ack.py` 등 신규 테스트, 전체 스위트 819/819 통과
- 실측: codex 위임 job `d04ebcb5` 의 `started`/`completed` 가 `agent_blocked` 없이 수락; codex 포함 `--all-reviewer` 루프가 만장일치 PASS 로 종료
- 리뷰 루프 claude·agy·grok·codex 만장일치 PASS (`.agents/reports/*/report-{2d3f24e4,47a845e0,d7e66053,0ad7cb06}.md`)
- v4.4.2 락스텝 반영 (`0918917`)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
문제 개요 (Summary)
multi-agent-mux-loop가 codex 리뷰어에게 리뷰 job 을 보낼 때, 프롬프트가 실제로는 전달되었는데도send_keys_safe가 전달 실패로 판정합니다. job 은 즉시error(agent_blocked) 로 종결되고, codex 가 이후 발행하는started/completed이벤트는 terminal 상태라 CAS 에서 거부됩니다. 결과적으로 codex 의 판정(PASS/NOT PASS)은 루프에 한 번도 반영되지 않으며, codex 가 포함된--all-reviewer루프는 만장일치에 도달할 수 없습니다.재현 (Reproduction) — 3/3
run_loop.sh --creator creator-claude-01 --all-reviewer --max-loop 3(리뷰어: claude·agy·grok·codex) 실행 중 codex 리뷰 job 이 매 이터레이션마다 동일하게 실패:98093686(iter 1)error120ace35(iter 2)errora536fce2(iter 3)errorMonitoring job '<id>' for status changes (timeout: 5.0s)... Reviewer 'planner-reviewer-codex-01' job crashed.send_keys_safe: delivery unknown/failed (planner-reviewer-codex-01)/ERROR: 프롬프트 주입 실패 — 세션 'planner-reviewer-codex-01' (프롬프트 잠금 의심)Completion event rejected because the job was already terminal error.가 출력됨exit 1로 종료세부 원인 분석 (Detailed Root Causes)
.agents/skills/lib.shsend_keys_safe(2330행~), 특히 2400–2446행herdr agent prompt <target> <text> --wait --until working --timeout 5000sleep 2후herdr agent get의agent_status가working인지 1회 확인 (2442행)delivery unknown/failed출력 후return 4→ 호출 측에서 job 을error agent_blocked로 종결agent_status를working으로 보고하지 않음. codex 는 프롬프트를 정상 수신해 0–3초 내started를 발행했으므로, 전달 확인 신호(herdr agent_status)와 실제 전달 사이의 불일치로 보임 (codex 상태 전이 지연/미검출 여부는 미측정)영향 (Impact) — High
--all-reviewer/--reviewer루프에서 codex 판정이 무시되고crashed로 처리 → 만장일치 불가,--max-loop소진 후 실패제안 해결책 (Proposed Fix)
agent_status == working단일 신호에 의존하지 말고, 해당 job 의started이벤트 수신(또는 pane 에 프롬프트 에코 확인)도 성공 신호로 인정return 4(delivery unknown) 는 즉시error로 종결하지 말고 "unknown" 상태로 두고started이벤트를 짧은 유예 시간 동안 기다린 뒤 판정 (단일 주입 계약은 유지 — 재전송 없음)--until working타임아웃(5000ms)이 충분한지 측정 후 에이전트별 설정값으로 분리agent prompt가 timeout 을 반환하지만started가 도착하는 케이스에서 job 이error로 종결되지 않아야 함환경 정보 (Environment)
4d444ce(main)planner-reviewer-codex-01, gpt-6-astra high), herdr sessionmulti-agent-mux42dadf59도agent_blocked였으나 이후started가 없고 pane 이 이전 작업 후 "Generating..." 에 멈춘 상태여서 본 이슈와 별개로 판단함브랜치
fix/issue-9-14에서 해결 (main4d444ce기준, 병합 전).원인 실측: herdr 0.9.0 은 codex 에 프롬프트를 정상 전달한 뒤
agent prompt --wait --until working --timeout 5000에{"code":"timeout"}을 돌려주고, codex 의agent_status는 작업 중에도 계속idle로 보고됨.수정 내용
send_keys_safe:timeout/agent_prompt_stalled(= 입력은 전달됐고 상태 관측만 실패)이면SKS_DELIVERY_GRACE_S(기본 15초) 동안working상태, 제출 전엔 없던 pane busy 토큰(esc to interrupt), 제출 전 스냅샷 대비 pane 변화 중 하나를 전달 확인 신호로 인정. 신호가 없으면 종전대로 rc 4 fail-closed,transport_error등은 종전 의미 유지, 단일 주입 계약 유지 (497633f)_inject_acknowledged: 전달 미확인 시MAM_INJECT_ACK_GRACE_S(기본 10초) 동안 레지스트리를 관측해pending을 벗어났으면(에이전트의started)agent_blocked로 닫지 않고 진행 (497633f)_agent_still_working: herdrworking외에 pane busy 토큰 인정 (eaa49aa);_recent_disk_heartbeat:events.log의 최근 heartbeat 도 생존 근거로 인정하고 근거마다 idle 창을 재개방, 벽시계START_TIME+TIMEOUT에서 확정 종료 (d5a191c,952657e)publish_event: 이벤트를 브로커 발행 전에 로컬events.log에 기록 (ebe58f6)검증
tests/test_issue14_delivery_ack.py등 신규 테스트, 전체 스위트 819/819 통과d04ebcb5의started/completed가agent_blocked없이 수락; codex 포함--all-reviewer루프가 만장일치 PASS 로 종료.agents/reports/*/report-{2d3f24e4,47a845e0,d7e66053,0ad7cb06}.md)0918917)