- 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
93 lines
7.4 KiB
Markdown
93 lines
7.4 KiB
Markdown
# 📑 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) 완료 후 운영에 투입됩니다.
|