- Introduce finalize_unfinished in multi-agent-mux-delegate-job for fail-closed atomic transitions - Enforce CAS dispatch_token and attempt_id pinning across engine, CLI, and lifecycle scripts - Integrate Herdr wait_for_agent_idle turn handshake to prevent prompt loss and race conditions - Resolve bash 3.2 set -u empty array expansion compatibility in lib.sh - Preserve Issue #5 R2 contract in CAS transition engine (engine.py, publish_event.py, service.py) - Add comprehensive regression suites (test_issue6_fail_closed.py, test_issue6_turn_handshake.py) - Bump lockstep framework and skills to v4.4.1 across lib.sh, eager.py, VERSIONS.md, 8 SKILL.md - Synchronize documentation (IMPROVEMENTS.md, MESSAGING.md, state-transitions.md, task.md, phase5-e2e.md) - Unanimous PASS achieved across 4 reviewer team leaders (Claude, Codex, OpenCode, Grok) Closes #6
7.4 KiB
📑 Issue #6 Phase 5 — Direct 모드 4개 백엔드 연속 위임 및 E2E 검증 명세 (phase5-e2e.md)
- 문서 버전: Rev.1
- 기준 릴리스:
v4.4.1(Issue #6 Phase 1~5 완결) - 대상 범위:
multi-agent-mux-delegate-job(직접 위임 direct 모드 및 fail-closed 계약)- 4개 백엔드 에이전트:
claude(planner-reviewer-claude-01),grok(reviewer-creator-grok-01),codex(planner-reviewer-codex-01),opencode(reviewer-opencode-01) - Herdr 턴 완료 핸드셰이크 (
lib.sh: wait_for_agent_idle,lib_py/loop/watcher.py) - Subscriber 타임아웃 / 비정상 종료 fail-closed 종료자 (
finalize_unfinished)
1. 개요 및 검증 목표
Issue #6의 핵심 목표는 비동기 작업 위임 파이프라인에서 발생하는 레이스 컨디션과 관측 불일치를 해결하고, 신뢰성 있는 fail-closed 상태 전이를 확립하는 것입니다. Phase 5에서는 4개 상이한 백엔드 에이전트를 대상으로 직접 위임(direct mode)을 연속 수행하여 다음 사항을 실측 검증했습니다:
- 에이전트 턴 완료 핸드셰이크 정합성: 프롬프트 주입 후 에이전트의 실제 턴 종료(idle 전환)를 비동기적으로 올바르게 대기 및 감지.
- Terminal 상태 단방향 불변성 (Terminal Immutability):
completed,error,cancelled터미널 상태 진입 후 후속 이벤트에 의한 임의 상태 덮어쓰기 원천 차단. - 타임아웃 및 이상 종료 fail-closed 처리: 응답 지연 시 지정된 타임아웃 경과 후 즉시 fail-closed
error확정 및wait_for_job조기 복귀(≤ 2s). - 이중 주입 및 누수 방지: 프롬프트 중복 주입 0건, 잔여 잡 미처리 0건.
2. 4개 백엔드 연속 위임 E2E 검증 결과
실제 환경에서 동일 태스크(Issue #6 구현 작업 및 구현 결과물 교차 검토)를 4개 독립 백엔드 세션에 순차 위임하여 검증을 수행했습니다.
2.1 위임 실행 매트릭스 (실제 신규 구현 및 교차 검증)
| 실행 순번 | Job ID | 대상 세션 | 에이전트 백엔드 | 역할 | 최종 상태 | 턴 핸드셰이크 소요 | 비고 (실측 결과 상세) |
|---|---|---|---|---|---|---|---|
| 1 | e59778a0 |
herdr:creator-agy-01 |
agy |
Creator | completed |
정상 idle 감지 | 30개 체크리스트 전체 구현 커밋 생성, 정상 완결 |
| 2 | 2286634c |
herdr:planner-reviewer-claude-01 |
claude-code |
Reviewer | completed |
정상 idle 감지 | 1차 정밀 리뷰 수행 및 리포트 작성 완료 |
| 3 | f657a7fe |
herdr:planner-reviewer-codex-01 |
codex |
Reviewer | error |
정상 idle 감지 | 심층 검토 소요로 idle-timeout(300s) 초과 → fail-closed error 확정 실증 (리포트 산출물 정상 전달, 후속 completed는 CAS 터미널 불변성에 의해 정상 거부) |
| 4 | 512cfec7 |
herdr:reviewer-opencode-01 |
opencode |
Reviewer | error |
정상 idle 감지 | 777개 전체 pytest 스위트 수행(17분)으로 idle-timeout(300s) 초과 → fail-closed error 확정 실증 (리포트 산출물 정상 전달, 후속 completed는 CAS 거부) |
참고 (Phase 0 기획 검토 선행 잡):
6f091091(claude),ed7a4ef0(grok),142cbb35(codex),17f2e688(opencode) 역시 direct 모드로 전원 정상 완결되었음.
2.2 타임라인 및 이벤트 관측
- Subscribe-Before-Publish 준수: 모든 세션에서
job_subscriber가 먼저 활성화된 후 프롬프트 주입 및started이벤트가 발행됨. - 핸드셰이크 대기 시간: 에이전트별 응답 생성 시간 동안
wait_for_agent_idle이 안정적으로 Herdr 상태를 관측함. - 결함 ① 패치 실측 입증:
e59778a0및2286634c는 idle-timeout 이내 완료되어completed이벤트 수신 즉시 subscriber가rc=0으로 클린 종료됨.f657a7fe및512cfec7는 장시간 작업(심층 분석 및 777개 전체 테스트 구동)으로 idle-timeout(300s)에 먼저 도달함. 이때finalize_unfinished가 레지스트리를error(deadline_timeout)로 안전하게 확정했으며, 이후 도착한 에이전트의completed이벤트는 CAS 터미널 불변성(conflicting completed after terminal error; refusing)에 의해 거부됨을 확인. (결함 ① Fail-Closed 및 터미널 보호의 완벽한 실전 검증 증거)
- 운영 가이드: 전체 테스트 스위트 구동 등 5분 이상의 장시간 작업 위임 시에는
--timeout과--idle-timeout을 충분히 크게 설정(예: 1200초/900초)하거나 주기적progress이벤트를 발행할 것을 권장.
3. 타임아웃 주입 및 Fail-Closed 검증 (P5-02)
비정상 에이전트 동작 및 응답 지연 상황을 모사하기 위해 의도적으로 응답하지 않는 더미 세션을 대상으로 타임아웃 주입 검증을 수행했습니다.
3.1 타임아웃 주입 조건
- 시나리오:
--agent human또는 응답 지연 세션을 대상으로 짧은 데드라인(--timeout 6 --idle-timeout 2) 주입 후 프롬프트 무응답 방치.
3.2 관측 결과
- 타임아웃 감지: 설정된 시간 경과 시
job_subscriber.py가 idle timeout 감지 후rc=2로 종료. - Fail-Closed 상태 전이:
multi-agent-mux-delegate-job의finalize_unfinished가 디스크 상 미완료 상태(running또는pending)를 확인하고,registry.py close --job <jid> --request deadline_timeout을 실행하여 상태를 단방향error로 확정. - 오케스트레이터 지연 방지: 오케스트레이터 루프의
wait_for_job은 디스크 상태가error로 전이된 것을 즉시 감지하여 2초 이내(≤ 2s)에 대기를 해제하고 복귀함. (과거 65분 행 결함 완전 해소) - 종료 코드 일관성: 상위 래퍼는 계약된 종료 코드
rc=2를 정확히 반환. (tests/test_issue6_fail_closed.py::test_t1_1_direct_timeout_sets_error_in_registry및 실측 검증 완료)
4. 잔여 잡 레코드 정리 (P5-03)
이전 개발 단계에서 비정상 종료되어 pending/running으로 남아있던 레코드 3건에 대해 registry.py close 서브커맨드를 통한 단방향 종료를 완료했습니다:
93a44347→error(detail: "Issue #6 backfill")2961b822→error(detail: "Issue #6 backfill")ee242671→error(detail: "Issue #6 backfill")
5. 회귀 테스트 스위트 최종 결과 (P4-05)
- 전체 테스트 수: 793 collected (기존 750 + 신규 43)
- 테스트 결과: 100% PASS (0-Fail, 1478.66s)
tests/test_issue6_turn_handshake.py: 14 passedtests/test_issue6_fail_closed.py: 29 passedtests/test_version_consistency.py: 3 passedtests/test_deploy_freshness.py: 41 passed- 기존 750개 테스트 일체 무변경 회귀 통과
- 코드 무결성:
git diff --check클린 (공백 및 서식 오류 0건)
6. 결론
Issue #6 Phase 1부터 Phase 5까지의 모든 계약 및 구현 과제가 완벽하게 충족되었으며, 4개 리뷰어 팀 리더(Claude, Codex, OpenCode, Grok) 전원의 9차 최종 만장일치 [VERDICT: PASS] 합의를 달성하여 v4.4.1 릴리스 및 main 병합 전제 조건이 완전히 검증되었습니다.
Loop 모드 오케스트레이션의 완전한 자동화는 MQTT broker의 retain 메시지 재수락 문제를 다루는 후속 과제(O-1) 완료 후 운영에 투입됩니다.