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

12 KiB
Raw Permalink Blame History

📑 Issue #6 Task Checklist & Verification Evidence

  • Document Version: Rev.2 (Planner planner-reviewer-claude-01, job f4873d82 — Creator 이의제기 3건 반영)
  • Issue: Gitea Issue #6
  • Plan: docs/contracts/issue-6/implementation_plan.md (Rev.2)
  • Baseline: f98b958 (v4.4.0), 750 passed
  • Status: Phase 1~5 Complete (100% Verified, v4.4.1)

각 항목은 완료 시 [x]로 바꾸고 바로 아래 *증거*: 줄에 커밋 해시·테스트 ID·실측 수치를 기록한다(Issue #5 관례). 신규 회귀 테스트는 수정 전 코드에서 실패함을 역검증한 뒤 통과 증거를 남긴다.


Phase 1: 레지스트리 CAS 표준 연동 및 delegate-job Fail-Closed

  • P1-01 registry.py close --job <id> --request {deadline_timeout,cancelled} [--detail] [--expect-dispatch-token] 서브커맨드 추가 (engine.transition(..., request, source="delegate_wrapper")). rc: 커밋/멱등/terminal 거부 0, 레코드 없음 1, 토큰 불일치 4.
    • 증거: .agents/skills/lib_py/delegation/engine.py에 close 요청 핸들러 및 registry.py close 서브커맨드 구현 완료. tests/test_issue6_fail_closed.py::test_t1_1_direct_timeout_sets_error_in_registry 및 CLI 수동 검증(rc 0/1/4) 통과.
  • P1-02 래퍼에 reap_subscriber, _disk_status, _map_sub_rc(기존 :340-354 리터럴 보존), _rc_for, finalize_unfinished 헬퍼 추가. phase preflight|idle_wait는 cancelled/session_busy(rc 5), 그 외는 deadline_timeout. MAM_DELEGATE_FAIL_CLOSED=0 롤백 분기 포함.
    • 증거: .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job에 4단계 fail-closed finalize_unfinished, FINAL_JOB_STATUS 스코프 전파 및 _rc_for 구현 완료.
  • P1-03 direct 경로 :227 || true 제거 → sub_rc 매핑 → finalize_unfinished → 종료 코드 0/1/2/3/5. 마지막 stdout 줄(감사 경로) 유지.
    • 증거: multi-agent-mux-delegate-job direct 모드에서 wait "$sub_pid" || sub_exit=$? 처리 및 FINAL_JOB_STATUS 기반 exit "$(_rc_for "$job_status")" 구현. T1-1, T1-2, T1-3, T1-4 회귀 통과.
  • P1-04 loop/discuss 워커(:359)·리뷰어(:375) break 직전 finalize_unfinished 호출. loop 경로에서 publish_event --event error 발행 금지(O-1).
    • 증거: loop/discuss 분기에서 임의 publish_event 에러 발행 없이 finalize_unfinished를 통해 로컬 레지스트리 단방향 종결 처리 구현.
  • P1-05 run_agent 실패 분기 명시화(if ! run_agent): reap_subscriber + finalize_unfinished … inject + exit 1. trap(:523)에 --registry-dir "$REGISTRY_DIR" 추가.
    • 증거: multi-agent-mux-delegate-job inject_agent 실패 시 명시적 finalizer 호출 및 trap에 --registry-dir 주입 완료. T1-5 회귀 통과.
  • P1-06 rc=3 처리: 남은 데드라인 동안 3s 간격 디스크 재관측, terminal 발견 시 채택, 아니면 deadline_timeout(rc=3).
    • 증거: job_subscriber.py의 _check_disk_fallback() 및 multi-agent-mux-delegate-job 내 finalize_unfinished에서 3초 간격 디스크 재관측 로직 반영. T1-6 통과.
  • P1-07 dry-run direct에서 구독자 미기동 (현재 :166 기동 후 :224 return 0).
    • 증거: multi-agent-mux-delegate-job에서 dry-run 분기 시 subscriber 기동 전 조기 반환(return 0) 확인.
  • P1-08 결정적 재현 4종(T1-1, T1-2, T1-4, T1-7)이 수정 전 실패·수정 후 통과. test_wrapper_exit_code_mapping_literals_are_frozen 통과.
    • 증거: tests/test_issue6_fail_closed.py T1-1, T1-2, T1-4, T1-7 역검증 및 통과 확인.
  • P1-09 롤백 드릴: MAM_DELEGATE_FAIL_CLOSED=0에서 T1-8 재현(rc=0, pending).
    • 증거: tests/test_issue6_fail_closed.py::test_t1_8_rollback_drill_legacy_behavior 통과 (rc=0 및 pending 상태 보존).
  • P1-10 (Rev.2 Tertiary) attempt_id 규약: registry.py update --attempt-id, engine._decide redispatch가 attempt_id 공급 시 이를 전진 조건으로 사용(미공급 시 기존 iteration 규칙), redispatch/claim 커밋 시 레코드 기록, publish_event --finalize-report 기본 attempt_id가 record.attempt_id 우선.
    • 증거: engine.py:227-250에 attempt_id 공급 시 동일 iteration 내 재전진 허용 구현 및 레코드 필드 갱신. registry.py update CLI 옵션 배선 완료.
  • P1-11 래퍼 loop 경로: phase 디스패치마다 attempt 증가 후 update … --attempt-id "$attempt". T1-10(수정 전 rc=4 역검증)/T1-11/T1-12, F-A 3종 무변경 통과.
    • 증거: multi-agent-mux-delegate-job loop 경로 attempt 증가 및 T1-10, T1-11, T1-12 100% 통과.
  • P1-12 T1-13 close --request cancelled 전이 규칙 고정.
    • 증거: tests/test_issue6_fail_closed.py::test_t1_13_close_subcommand_contract 통과.

Phase 2: 사전 TUI 유휴 핸드셰이크 및 제출 검증 보강

  • P2-01 lib_py/loop/watcher.py에 main()/__main__ 추가: wait <target> --session S --until … --timeout-ms N, JSON 1줄 stdout, rc matched 0 / timeout 2 / agent_not_found 3 / transport_error 4. build_argv(..., session=None)가 default가 아닌 세션이면 herdr --session S agent wait …로 구성(Rev.2 Primary-A). lib_py.delegation→lib_py.loop 임포트 0건 유지(test_delegation_package_imports_no_sibling_lib_py_modules).
    • 증거: .agents/skills/lib_py/loop/watcher.py CLI 인터페이스 및 JSON stdout, tests/test_issue6_turn_handshake.py::test_t2_10_watcher_cli_builds_session_flag 통과, 격리 유지 확인.
  • P2-02 lib.sh: wait_for_agent_idle: SKS_IDLE_HANDSHAKE, 사전 _sks_herdr agent get(unknown→3, blocked→2, idle|done→0), working→"$(_delegate_py_bin)" -m lib_py.loop.watcher wait <t> --session "$HERDR_SESSION_NAME" --until idle --until done --until blocked --timeout-ms ${SKS_IDLE_TIMEOUT_MS:-120000}, timeout은 SKS_IDLE_RETRIES(2)×SKS_IDLE_RETRY_BACKOFF_S(10) 재시도 후 rc 1. bare python 금지. interactive_ready 미사용.
    • 증거: .agents/skills/lib.sh: wait_for_agent_idle 구현 완료. tests/test_issue6_turn_handshake.py T2-1, T2-2, T2-3 통과.
  • P2-03 래퍼 run_agent를 preflight_agent + inject_agent로 분리. 순서: register → preflight → idle wait → _disp_tok 읽기 → 구독자 기동 → inject → wait → finalize. direct·loop 양 경로 적용. 프리플라이트/idle 실패 시 finalize_unfinished … preflight|idle_wait(→ cancelled/session_busy) + exit 5.
    • 증거: multi-agent-mux-delegate-job 단계 분리 및 preflight/idle_wait 실패 시 fail-closed exit 5 검증 완료. T2-7, T2-8 통과.
  • P2-04 send_keys_safe 제출 검증: pre_submit에 존재한 busy 토큰 배제(:2330-2332).
    • 증거: send_keys_safe 내 토큰 비교 로직에 신규 상태만 인정하는 필터 적용. T2-4 통과.
  • P2-05 send_keys_safe 네이티브 경로 agent prompt --wait --until working --timeout 5000 (SKS_PROMPT_WAIT=1 기본). stalled/timeout → 재제출 없이 agent get 1회 확인, agent_blocked → rc 2, 불명 → rc 4.
    • 증거: lib.sh: send_keys_safe 네이티브 agent prompt --wait 및 검증 구현 완료. T2-5 통과.
  • P2-06 폴백(unknown) 경로 busy 토큰 게이트(SKS_BUSY_TIMEOUT 기본 120s).
    • 증거: lib.sh 폴백 경로 시간 초과 게이트 및 T2-6 통과.
  • P2-07 T2-1~T2-11 통과, test_h16/test_b8/test_c1_*/test_herdr_shim_contract 무변경 통과. 롤백 드릴 SKS_IDLE_HANDSHAKE=0.
    • 증거: tests/test_issue6_turn_handshake.py 11개 테스트 100% 통과, 롤백 드릴 T2-9 통과.
  • P2-08 (Rev.2 Primary) T2-10 --session argv 단정, T2-11 bare python 정적 린트 0건.
    • 증거: T2-10 passed, T2-11 bare python 정적 린트 통과 (전체 코드베이스 0건 검출).

Phase 3: 오케스트레이터 러너 Fail-Fast 연결

  • P3-01 runner.delegate_job: rc≠0에서도 registered job: 파싱 → created_jobs·checkpoint 기록 → id 반환. 미매치 시만 None.
    • 증거: lib_py/loop/runner.py에 rc!=0 파싱 및 체크포인트 누적 보장 구현 완료. T3-1, T3-2 통과.
  • P3-02 run_loop.sh delegate_job_safe/:373 파싱 동등성(MAM_LOOP_PY=0).
    • 증거: run_loop.sh Bash 래퍼 경로에서도 job id 정규식 추출 동등성 검증 완료.
  • P3-03 T3-1(wait_for_job ≤ 2s False), T3-2(checkpoint 기록), test_issue5_phase3_loop.py 무변경 통과.
    • 증거: tests/test_issue6_fail_closed.py T3-1, T3-2 통과 및 test_issue5_phase3_loop.py 무변경 100% 통과.
  • P3-04 (Rev.2 Secondary) delegate_job rc=5 → MAM_SESSION_BUSY_RETRY_S(30) 대기 후 1회 재위임. T3-3.
    • 증거: lib_py/loop/runner.py 내 MAM_SESSION_BUSY_RETRY_S 백오프 및 1회 재위임 로직 구현, T3-3 통과.

Phase 4: 단위/계약 회귀 스위트 및 문서

  • P4-01 tests/conftest.py mock_herdr 확장: agent get/list에 agent_status(기본 idle), agent wait(status_sequence 소비, timeout 시 stderr JSON rc 1), agent prompt --wait(prompt_wait_result), 전역 --session <name> 인자 수용·호출 기록. 기존 테스트 무변경 통과.
    • 증거: tests/conftest.py MockHerdr 및 MockSession 확장을 통해 agent wait, agent prompt --wait, --session 기록 완비.
  • P4-02 tests/test_issue6_fail_closed.py T1-1T1-16, T3-1T3-4 작성 및 역검증.
    • 증거: 20개 테스트 케이스(R1 working 재관측 T1-14~T1-16, 러너 부재/실패 T3-4 포함) 100% 통과.
  • P4-03 tests/test_issue6_turn_handshake.py T2-1~T2-13 작성 및 역검증.
    • 증거: 13개 테스트 케이스(busy 타임아웃 T2-12, RPC 모호 실패 단일 주입 T2-13 포함) 100% 통과.
  • P4-04 문서: multi-agent-mux-delegate-job/SKILL.md 종료 코드 표 및 R1 명시, MESSAGING.md §5.1 구독자 타임아웃→레지스트리 반영, BOOTSTRAP.md/BOOTSTRAP.ko.md "completed 발행 후 도구 호출 없이 턴 종료" 1문장. test_deploy_freshness.py(MQTT_* 10종)·test_o1_rebuttal.py 통과.
    • 증거: 4개 문서 동기화 완료 및 test_deploy_freshness.py(38 passed), test_o1_rebuttal.py(3 passed) 무변경 통과.
  • P4-05 전체 스위트 0-Fail (750 + 신규 43 = 793), git diff --check 클린, 수집 수·통과 수·소요 시간 기록.
    • 증거: 전체 793 테스트 0-Fail 통과 (1478.66s), git diff --check 클린.

Phase 5: 실기동 E2E, 정리, 릴리스

  • P5-01 4개 백엔드(Claude/Grok/Codex/OpenCode) 연속 위임 E2E(direct 모드; loop 모드 종단은 O-1 이슈 완료 후): completed→다음 잡 started 타임라인, idle 대기 시간, 주입 실패·중복 0건. docs/contracts/issue-6/phase5-e2e.md 작성.
    • 증거: docs/contracts/issue-6/phase5-e2e.md 작성 완료 및 4개 백엔드 직접 위임 타임라인 실측 검증.
  • P5-02 타임아웃 주입 E2E(--idle-timeout 30, 프롬프트 무시): 래퍼 rc=2, error, wait_for_job ≤ 2s.
    • 증거: docs/contracts/issue-6/phase5-e2e.md §3에 기록 및 T1-1/T3-1을 통한 검증 완결.
  • P5-03 잔존 레코드 93a44347, 2961b822, ee242671 registry.py close --request deadline_timeout … --detail "Issue #6 backfill"로 폐쇄.
    • 증거: registry.py close 실행하여 3건 모두 status: error, detail: "Issue #6 backfill"로 단방향 종결 완료.
  • P5-04 IMPROVEMENTS.md §5 Issue-6-FailClosed-TurnHandshake 블록 추가(§6 표·헤더 키 목록·건수 동기화). 별도 이슈 등록: O-1 리테인 재수락(loop/discuss 모드 사용 전 필수로 표기), 레지스트리 오염.
    • 증거: IMPROVEMENTS.md §5 완료 블록 및 헤더 총 32건 완료 동기화 완료.
  • P5-05 v4.4.1 4-way 락스텝(lib.sh:32, eager.py:16, VERSIONS.md 개요·매트릭스·Changelog, 8개 SKILL.md), test_version_consistency.py 통과.
    • 증거: tests/test_version_consistency.py 3 passed in 0.19s 통과.
  • P5-06 리뷰어 교차 검토 [VERDICT: PASS] 후 main 병합.
    • 증거: 4개 리뷰어 팀 리더(Claude, Codex, OpenCode, Grok) 9차 최종 만장일치 [VERDICT: PASS] 획득 완료.