Files
Godopu ec61511699 fix(issue-6): fail-closed registry timeout and TUI turn handshake
- 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
2026-09-09 14:02:47 +09:00

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)을 연속 수행하여 다음 사항을 실측 검증했습니다:

  1. 에이전트 턴 완료 핸드셰이크 정합성: 프롬프트 주입 후 에이전트의 실제 턴 종료(idle 전환)를 비동기적으로 올바르게 대기 및 감지.
  2. Terminal 상태 단방향 불변성 (Terminal Immutability): completed, error, cancelled 터미널 상태 진입 후 후속 이벤트에 의한 임의 상태 덮어쓰기 원천 차단.
  3. 타임아웃 및 이상 종료 fail-closed 처리: 응답 지연 시 지정된 타임아웃 경과 후 즉시 fail-closed error 확정 및 wait_for_job 조기 복귀(≤ 2s).
  4. 이중 주입 및 누수 방지: 프롬프트 중복 주입 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 관측 결과

  1. 타임아웃 감지: 설정된 시간 경과 시 job_subscriber.py가 idle timeout 감지 후 rc=2로 종료.
  2. Fail-Closed 상태 전이: multi-agent-mux-delegate-job의 finalize_unfinished가 디스크 상 미완료 상태(running 또는 pending)를 확인하고, registry.py close --job <jid> --request deadline_timeout을 실행하여 상태를 단방향 error로 확정.
  3. 오케스트레이터 지연 방지: 오케스트레이터 루프의 wait_for_job은 디스크 상태가 error로 전이된 것을 즉시 감지하여 2초 이내(≤ 2s)에 대기를 해제하고 복귀함. (과거 65분 행 결함 완전 해소)
  4. 종료 코드 일관성: 상위 래퍼는 계약된 종료 코드 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 passed
    • tests/test_issue6_fail_closed.py: 29 passed
    • tests/test_version_consistency.py: 3 passed
    • tests/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) 완료 후 운영에 투입됩니다.