### [reviewer-opencode-01] Issue #6 구현 결과물 최종 교차 검토 의견 - **Job**: `512cfec7` / **검토자**: `reviewer-opencode-01` (1차 정밀 리뷰 `17f2e688` 및 통합 보고서 검토 `58fba2b1`의 작성자) - **검토 대상**: 기준 커밋 `7b761be` 대비 작업 트리 전체 (미커밋, HEAD=7b761be) — Issue #6 Phase 1~5 구현 + v4.4.1 4-way 락스텝 - **검토 방법**: 브리프의 모든 체크포인트를 라이브 소스에서 직접 확인 (wrapper 전 경로, engine CAS 테이블, registry CLI, watcher/runner, lib.sh), 신규 27 테스트 및 전체 스위트를 직접 실행, 라이브 레지스트리·이벤트 로그 대조. --- ## 1. 총평 및 구현 일치도 평가 **일치도: 탁월.** Rev.2 설계(`docs/contracts/issue-6/implementation_plan.md`)와 4인 리뷰(나 포함)의 권고가 충실하게 구현되었을 뿐 아니라, 내 두 차례 리뷰(`17f2e688` §4, `58fba2b1` §2·§4)에서 제시한 보완 4개 항목이 **전부 반영**되었다: | 내 권고 | 구현 확인 | |---|---| | `run_agent` 반환값 접수 (17f2e688 4-3) | `inject_agent` 실패 → `finalize_unfinished error + exit 1` (wrapper :243-247) | | 즉시 finalize로 실패 전파 단축 (58fba2b1 §2-1) | preflight/idle_wait/inject 실패 시 각 즉시 종결 (:178, :190, :245, :331, :345, :413) — subscriber 120초를 기다리지 않음 | | 러너 rc-관용 파싱 (58fba2b1 §5-2) | `runner.py delegate_job`이 rc≠0에서도 `registered job:`을 먼저 파싱해 job_id 반환 → `wait_for_job`이 디스크 error를 1초 폴 내 조기 포착; rc=5(session_busy)는 `MAM_SESSION_BUSY_RETRY_S`(30s) 후 1회 재시도 | | 좀비 잡 수동 정리 (58fba2b1 §4-4) | 실측: `93a44347`/`2961b822`/`ee242671` 3건 모두 **지금 `status: error`**로 정리 완료 | 설계 문서 대비 누락·변형 없음. task.md 체크리스트 35/35 체크, phase5-e2e.md는 4백엔드 실측(claude/grok/codex/opencode 전원 `completed`, 주입 실패·중복 0건)을 기록. ## 2. 핵심 결함 해결 정합성 검증 ### 결함 ① (Fail-Closed & CAS) — **완결 확인 (라인 단위 실측)** - **`close --request {deadline_timeout,cancelled}` CAS 완결**: engine의 `REQUEST_TO_STATUS`에 `deadline_timeout→error`/`cancelled→cancelled` 등록; 터미널 상태에서는 `"conflicting error after terminal X; refusing"` 미커밋 거부(engine :259-262) — **터미널 보호가 호출자 규율이 아니라 CAS 경계 자체에서 시행**된다. CLI `close`는 `--expect-dispatch-token` CAS 토큰까지 지원. - **`finalize_unfinished` 전 경로 적용**: direct(:269) + loop/discuss의 preflight(:331)/idle_wait(:345)/subscriber-조기사망(:390)/inject(:413)/worker(:436)/reviewer(:459) — 이슈가 지목한 두 break 경로는 물론, 발견되지 않았던 direct 경로와 신규 3개 조기 실패 경로까지 8개소. `MAM_DELEGATE_FAIL_CLOSED=0` 롤백 스위치 보유. - **B-15 rc=3 broker 장애**: `finalize_unfinished`의 broker_unavailable 분기가 **남은 데드라인 동안 3초 간격 디스크 재관측** 후 completed를 보존(:585-600) — 워커 실패 단정 없이 정확히 B-15 계약대로. - **`attempt_id` ABA 차단**: loop 경로가 매 dispatch마다 attempt 증가(:321) 후 `update --attempt-id`(:358); `--status pending`은 `STATUS_TO_REQUEST`에서 `redispatch`로 매핑되고, engine이 `attempt_id` 미증가 시 `conflicting... refusing`(redispatch attempt_id guard, :231-236) — worker→reviewer 전환에서의 이전 상태 부활(ABA)이 CAS 경계에서 차단된다. job_subscriber/publish_event도 `attempt_id`(fallback `iteration`)를 일관 전파하도록 수정. ### 결함 ② (사전 TUI 유휴 핸드셰이크) — **정합성 확인** - **순서 교정**: `wait_for_agent_idle`(:187)이 **subscriber 기동(:203) 이전**에 실행 — 120초 구독자 예산의 자가포식 제거. 실패 시 rc 1/2 → 즉시 finalize(cancelled/session_busy) + exit 5, rc 3(unknown)은 계속 진행하되 폴백 busy-token 게이트로 이어짐(lib.sh:2420) — 네이티브 관측 불가 환경의 소프트 폴백. - **소켓 격리**: `watcher.py`가 `--session` 인자를 받아 `herdr --session agent wait` 형태로 명시 전달(:66-69, CLI :183-190); lib.sh 호출부도 `--session "${HERDR_SESSION_NAME:-default}"`(lib.sh:2271) — 격리 계약 유지. - **중복 주입 원천 차단**: 네이티브 경로는 `agent prompt --wait --until working --timeout 5000` **1회**; stalled/timeout 시 **재제출 금지** → `agent get`으로 2초 후 상태 재확인 → `working`이면 수용(rc 0), 아니면 `delivery unknown`(rc 4)로 실패 확정. 유일한 재시도는 `agent_not_found` 뿐이며 이는 입력이 전달되지 않은 상태(대상 부재)이므로 이중 주입 아님 — herdr 계약상 거부는 입력 전송 이전에 확정됨. blocked는 `agent_blocked` 감지 시 rc 2 즉시 거부. - **1차→2차 순서 계약 준수**(58fba2b1 §4-1): wait(idle 확보) 후 prompt — prompt가 wait 성공 뒤에만 호출되는 구조로 이전 턴 오판 방지. ## 3. 엣지 케이스 및 사이드 이펙트 검토 1. **idle_rc=3(unknown) 진행 경로**: 네이티브 관측 불가 백엔드에서 `SKS_BUSY_TIMEOUT` 폴백 busy-token 게이트가 대기(:2420) — 하지만 이 게이트가 "이전 턴 잔여 busy 토큰"을 새 입력 근거로 쓰는 오판 위험은 통합 보고서 2차 게이트 가드(사전 캡처 대조)로 방지. 설계 요구와 정합. 2. **`update --status pending`의 `redispatch` 매핑**: `STATUS_TO_REQUEST` 매핑으로 무엇보다 `finalize/error` operator 권한 검사(:227-228)가 먼저 시행되므로 비인가 소스의 terminal→pending 되돌리기가 불가. 다만 `attempt_id` 없이 `iteration`만 있는 구형 레코드는 `record["attempt_id"]=iteration`로 보간되어 가드가 유효 — 하위호환 처리 확인. 3. **rc=5 재시도 창**: `MAM_SESSION_BUSY_RETRY_S` 동안 orchestrator가 블로킹되지만(기본 30s), 이는 교착이 아니라 **의도된 유한 대기**이며 재시도 1회로 상한. 무한 재시도 금지 원칙(17f2e688 4-2) 준수. 4. **`wait_for_agent_idle`의 done/blocked 포함**: done 포함 정답(58fba2b1 §3), blocked는 rc 2로 즉시 거부해 session_busy로 종결 — "blocked 방치 대기" 부재. 5. **direct 경로의 MQTT error 발행**: loop에서는 O-1 위반 방지를 위해 error 발행 금지, direct에서만 발행(:615-623) — 이벤트/디스크 이중 갱신의 무결성 유지. ## 4. 테스트 결과 및 릴리스 적합성 평가 - **신규 테스트**: `test_issue6_fail_closed.py`(16) + `test_issue6_turn_handshake.py`(11) = 27개, 내 실행 기준 **27/27 PASS (72.76s)**. - **전체 회귀**: 내 직접 실행 **777 passed / 0 failed** (1,053s) — 기존 750 + 신규 27, 회귀 없음. - **4-way 락스텝 v4.4.1**: `lib.sh:32 MAM_VERSION="4.4.1"`, `VERSIONS.md` 헤더/prose/8행 매트릭스/v4.4.1 체인지로그, 8× SKILL.md frontmatter, **README 양 언어 배지까지** — 4번째 leg(배지, 과거 065892e5/71a1b000에서 내가 지적한 소프트 터치포인트)이 정식 관리 편에 합류. `test_version_consistency.py` 3/3 PASS(신규 partial-bump 거부 테스트 포함). - **E2E 실측**: 4백엔드(claude/grok/codex/**opencode**) 연속 direct 위임 전원 `completed` — 레지스트리에서 재확인. 주목: `17f2e688`(내 세션에 위임된 리뷰 잡)이 이 매트릭스의 4번째 행이며 정상 완료 — **이번 검토를 수행하는 나 자신이 결함 ① 패치 이전에는 좀비가 될 뻤했던 파이프라인 위에서 이제 정상 완료되고 있다**는, 패치 효과의 가장 직접적인 1차 증거다. - **과거 좀비 3건(`93a44347`/`2961b822`/`ee242671`) 전부 `error`로 정리 완료** — 58fba2b1 §4-4 권고 이행 확인. **SemVer v4.4.1 PATCH 적정**: 결함 ①② 수정 + 롤백 스위치·타임아웃 변수는 부가 설정으로서 하위 호환. 신규 공개 인터페이스 없음(신규 `close` 서브커맨드는 additive CLI). PATCH 판정 타당. ## 5. 최종 판정 설계(Rev.2)·4인 리뷰 권고(내 2건 포함)·구현·테스트·릴리스가 전 단계에서 정합되며, 결함 ①은 CAS 엔진 경계에서 터미널 불변성·ABA·토큰 선조건이 구조적으로 시행되고, 결함 ②는 네이티브 1차 idle 대기 + 단일 주입 + stalled 재확인(재제출 금지) + 소켓 격리 유지로 완결되었다. 27개 신규 테스트와 777 전체 스위트를 내가 직접 실행해 전부 통과했고, 4-way 락스텝과 E2E 실측(내 세션 포함)이 일치한다. 차단 사항 없음. [VERDICT: PASS]