risk: define and implement kill-switch refresh retry policy (#377) #389

Merged
jihoson merged 2 commits from feature/issue-377-kill-switch-refresh-retry into feature/v3-session-policy-stream 2026-03-02 09:47:56 +09:00
Collaborator

Traceability

  • REQ-ID: REQ-V2-008
  • TASK-ID: TASK-V2-014
  • TEST-ID: TEST-ACC-002

Summary

  • Kill Switch refresh_order_state 단계에 재시도 정책을 추가했습니다.
    • 최대 시도: 3회(초기 1 + 재시도 2)
    • 간격: 지수 backoff (1s, 2s) 기본값
    • 중단조건: refresh 성공 시 즉시 다음 단계 진행
    • 소진조건: 재시도 모두 실패 시 오류를 report에 기록하고 reduce_risk 이후 단계는 계속 수행
  • _refresh_order_state_for_kill_switch()가 마켓별 refresh 실패를 집계해 예외를 raise 하도록 바꿔, 오케스트레이터의 재시도 정책이 실제로 적용되게 했습니다.
  • tests/test_kill_switch.py에 refresh 재시도 성공/소진 회귀 테스트 2건을 추가했습니다.
  • 요구사항 원장(REQ-V2-008) 문구를 재시도 정책과 정합화하고, 구현 감사 문서 상태를 ✅ 완료로 갱신했습니다.

Validation

  • python3 scripts/session_handover_check.py --strict
  • ruff check src/core/kill_switch.py src/main.py tests/test_kill_switch.py tests/test_main.py
  • pytest -q tests/test_kill_switch.py tests/test_main.py -k "kill_switch or refresh_order_state_for_kill_switch"
  • python3 scripts/validate_ouroboros_docs.py
  • python3 scripts/validate_docs_sync.py
  • BASE=$(git merge-base origin/feature/v3-session-policy-stream HEAD); python3 scripts/validate_governance_assets.py "$BASE...HEAD"
## Traceability - REQ-ID: `REQ-V2-008` - TASK-ID: `TASK-V2-014` - TEST-ID: `TEST-ACC-002` ## Summary - Kill Switch `refresh_order_state` 단계에 재시도 정책을 추가했습니다. - 최대 시도: 3회(초기 1 + 재시도 2) - 간격: 지수 backoff (`1s`, `2s`) 기본값 - 중단조건: refresh 성공 시 즉시 다음 단계 진행 - 소진조건: 재시도 모두 실패 시 오류를 report에 기록하고 `reduce_risk` 이후 단계는 계속 수행 - `_refresh_order_state_for_kill_switch()`가 마켓별 refresh 실패를 집계해 예외를 raise 하도록 바꿔, 오케스트레이터의 재시도 정책이 실제로 적용되게 했습니다. - `tests/test_kill_switch.py`에 refresh 재시도 성공/소진 회귀 테스트 2건을 추가했습니다. - 요구사항 원장(`REQ-V2-008`) 문구를 재시도 정책과 정합화하고, 구현 감사 문서 상태를 `✅ 완료`로 갱신했습니다. ## Validation - `python3 scripts/session_handover_check.py --strict` - `ruff check src/core/kill_switch.py src/main.py tests/test_kill_switch.py tests/test_main.py` - `pytest -q tests/test_kill_switch.py tests/test_main.py -k "kill_switch or refresh_order_state_for_kill_switch"` - `python3 scripts/validate_ouroboros_docs.py` - `python3 scripts/validate_docs_sync.py` - `BASE=$(git merge-base origin/feature/v3-session-policy-stream HEAD); python3 scripts/validate_governance_assets.py "$BASE...HEAD"`
agentson added 1 commit 2026-03-02 09:38:50 +09:00
risk: add kill-switch refresh retry policy and tests (#377)
All checks were successful
Gitea CI / test (push) Successful in 33s
Gitea CI / test (pull_request) Successful in 34s
ba2370e40e
Author
Collaborator

PR #389 코드 리뷰 — risk: define and implement kill-switch refresh retry policy (#377)

범위: kill-switch refresh_order_state 단계에 3회 exponential backoff 재시도 정책 추가


통과 항목

1. 재시도 로직 구현

  • _run_refresh_with_retry(): max_attempts 한도 내 exponential backoff (delay * 2^(attempt-1))
  • 재시도 소진 시 에러 기록 후 이후 단계(reduce_risk → snapshot) 계속 실행 (안전 폴백 유지)
  • trigger() 기본값: refresh_retry_attempts=3, refresh_retry_base_delay_sec=1.0

2. _refresh_order_state_for_kill_switch() 연동

  • failures 리스트 수집 후 하나라도 실패 시 RuntimeError raise → 재시도 메커니즘과 연동

3. 기존 함수 호환성

  • main.py의 step 함수들(lambda, void)은 모두 None 반환 → result is False 조건 미충족, 기존 동작 유지

4. 테스트 커버리지

  • test_kill_switch_refresh_retries_then_succeeds: 2번 실패 후 3번째 성공 → errors=[]
  • test_kill_switch_refresh_retry_exhausted_records_error_and_continues: 재시도 소진 → 에러 기록 후 reduce/snapshot 실행
  • 로컬 실행: 4 passed in 0.02s

5. 문서: REQ-V2-008 ⚠️, 재시도 정책(3회, 1s/2s backoff) 스펙 명시, 버전 1.0.11


⚠️ 소견 1 — _run_refresh_with_retry의 pragma: no cover 불일치 (Non-blocking)

L76:
except Exception as exc: # pragma: no cover - intentionally resilient

이 블록은 test_kill_switch_refresh_retries_then_succeeds와 test_kill_switch_refresh_retry_exhausted_records_error_and_continues 두 테스트 모두 실제로 실행합니다.
_run_step()의 except 블록은 테스트로 재현이 어려운 예외 경로라 pragma: no cover가 타당하지만,
_run_refresh_with_retry()는 테스트가 이미 이 경로를 커버합니다.

권장: _run_refresh_with_retry()의 except 블록에서 pragma: no cover 제거하면 커버리지 리포트가 실제 커버리지를 정확히 반영합니다.


⚠️ 소견 2 — failures[:3] 절삭 시 총 실패 수 미노출 (Non-blocking)

report.errors.append(
    "refresh_order_state: failed after "
    f"{attempts} attempts ({last_exc})"
)

_refresh_order_state_for_kill_switch()에서 failures[:3]으로 절삭하므로, 마켓이 4개 이상일 때 실제 실패 수를 알 수 없습니다.

권장:
f"; ".join(failures[:3]) + (f" (+{len(failures)-3} more)" if len(failures) > 3 else "")

현재 운용 마켓이 소수라면 Non-blocking 수준입니다.


결론

LGTM — 재시도 정책 구현 완료, 테스트 통과, 안전 폴백 유지.
소견은 모두 Non-blocking으로 별도 처리 가능합니다.

## PR #389 코드 리뷰 — risk: define and implement kill-switch refresh retry policy (#377) **범위**: kill-switch refresh_order_state 단계에 3회 exponential backoff 재시도 정책 추가 --- ### ✅ 통과 항목 **1. 재시도 로직 구현** - _run_refresh_with_retry(): max_attempts 한도 내 exponential backoff (delay * 2^(attempt-1)) - 재시도 소진 시 에러 기록 후 이후 단계(reduce_risk → snapshot) 계속 실행 (안전 폴백 유지) - trigger() 기본값: refresh_retry_attempts=3, refresh_retry_base_delay_sec=1.0 **2. _refresh_order_state_for_kill_switch() 연동** - failures 리스트 수집 후 하나라도 실패 시 RuntimeError raise → 재시도 메커니즘과 연동 **3. 기존 함수 호환성** - main.py의 step 함수들(lambda, void)은 모두 None 반환 → result is False 조건 미충족, 기존 동작 유지 ✅ **4. 테스트 커버리지** - test_kill_switch_refresh_retries_then_succeeds: 2번 실패 후 3번째 성공 → errors=[] ✅ - test_kill_switch_refresh_retry_exhausted_records_error_and_continues: 재시도 소진 → 에러 기록 후 reduce/snapshot 실행 ✅ - 로컬 실행: 4 passed in 0.02s ✅ **5. 문서**: REQ-V2-008 ⚠️ → ✅, 재시도 정책(3회, 1s/2s backoff) 스펙 명시, 버전 1.0.11 --- ### ⚠️ 소견 1 — _run_refresh_with_retry의 pragma: no cover 불일치 (Non-blocking) L76: except Exception as exc: # pragma: no cover - intentionally resilient 이 블록은 test_kill_switch_refresh_retries_then_succeeds와 test_kill_switch_refresh_retry_exhausted_records_error_and_continues 두 테스트 모두 실제로 실행합니다. _run_step()의 except 블록은 테스트로 재현이 어려운 예외 경로라 pragma: no cover가 타당하지만, _run_refresh_with_retry()는 테스트가 이미 이 경로를 커버합니다. 권장: _run_refresh_with_retry()의 except 블록에서 pragma: no cover 제거하면 커버리지 리포트가 실제 커버리지를 정확히 반영합니다. --- ### ⚠️ 소견 2 — failures[:3] 절삭 시 총 실패 수 미노출 (Non-blocking) report.errors.append( "refresh_order_state: failed after " f"{attempts} attempts ({last_exc})" ) _refresh_order_state_for_kill_switch()에서 failures[:3]으로 절삭하므로, 마켓이 4개 이상일 때 실제 실패 수를 알 수 없습니다. 권장: f"; ".join(failures[:3]) + (f" (+{len(failures)-3} more)" if len(failures) > 3 else "") 현재 운용 마켓이 소수라면 Non-blocking 수준입니다. --- ### 결론 **LGTM** — 재시도 정책 구현 완료, 테스트 통과, 안전 폴백 유지. 소견은 모두 Non-blocking으로 별도 처리 가능합니다.
agentson added 1 commit 2026-03-02 09:44:25 +09:00
risk: polish retry coverage and refresh failure summary
All checks were successful
Gitea CI / test (push) Successful in 32s
Gitea CI / test (pull_request) Successful in 31s
b34937ea9d
Author
Collaborator

리뷰 반영했습니다. (비블로킹 소견 2건)

  1. _run_refresh_with_retry coverage 표시 정합화
  • src/core/kill_switch.py_run_refresh_with_retry except 블록에서 # pragma: no cover를 제거했습니다.
  • 해당 경로는 테스트가 실제로 커버하므로 리포트가 실커버리지와 일치하도록 정리했습니다.
  1. refresh 실패 요약 개선
  • src/main.py::_refresh_order_state_for_kill_switch에서 실패 메시지를 상위 3건 + 초과 건수로 표기하도록 변경했습니다.
    • 형식: ...; ...; ... (+N more)
  • 회귀 테스트 추가: test_refresh_order_state_failure_summary_includes_more_count

검증:

  • ruff check src/core/kill_switch.py src/main.py tests/test_kill_switch.py tests/test_main.py
  • pytest -q tests/test_kill_switch.py tests/test_main.py -k "kill_switch or refresh_order_state_for_kill_switch or failure_summary_includes_more_count"
    • 결과: 9 passed
  • python3 scripts/validate_ouroboros_docs.py
  • python3 scripts/validate_docs_sync.py
  • BASE=$(git merge-base origin/feature/v3-session-policy-stream HEAD); python3 scripts/validate_governance_assets.py "$BASE...HEAD"
리뷰 반영했습니다. (비블로킹 소견 2건) 1) `_run_refresh_with_retry` coverage 표시 정합화 - `src/core/kill_switch.py`의 `_run_refresh_with_retry` except 블록에서 `# pragma: no cover`를 제거했습니다. - 해당 경로는 테스트가 실제로 커버하므로 리포트가 실커버리지와 일치하도록 정리했습니다. 2) refresh 실패 요약 개선 - `src/main.py::_refresh_order_state_for_kill_switch`에서 실패 메시지를 상위 3건 + 초과 건수로 표기하도록 변경했습니다. - 형식: `...; ...; ... (+N more)` - 회귀 테스트 추가: `test_refresh_order_state_failure_summary_includes_more_count` 검증: - `ruff check src/core/kill_switch.py src/main.py tests/test_kill_switch.py tests/test_main.py` - `pytest -q tests/test_kill_switch.py tests/test_main.py -k "kill_switch or refresh_order_state_for_kill_switch or failure_summary_includes_more_count"` - 결과: `9 passed` - `python3 scripts/validate_ouroboros_docs.py` - `python3 scripts/validate_docs_sync.py` - `BASE=$(git merge-base origin/feature/v3-session-policy-stream HEAD); python3 scripts/validate_governance_assets.py "$BASE...HEAD"`
Author
Collaborator

추가 리뷰 — risk: polish retry coverage and refresh failure summary

커밋: b34937e

소견 반영 확인

소견 1 (pragma: no cover 제거)
_run_refresh_with_retry()의 except 블록에서 pragma: no cover 제거 완료.
테스트가 실제로 이 경로를 실행하므로 커버리지 리포트가 정확해집니다.

소견 2 ((+N more) 절삭 표시)
_refresh_order_state_for_kill_switch()에 적용 완료:

summary = "; ".join(failures[:3])
if len(failures) > 3:
    summary = f"{summary} (+{len(failures) - 3} more)"
raise RuntimeError(summary)

추가로 _cancel_pending_orders_for_kill_switch()에도 동일하게 적용 — 소견 범위를 넘어선 일관성 개선

신규 테스트
test_refresh_order_state_failure_summary_includes_more_count:

  • 4개 마켓 전부 실패 → "(+1 more)" 말미 포함 검증 (match=r"(+1 more)$")
  • "KR/KRX" 첫 번째 실패 항목 포함 검증

로컬 실행: 5 passed in 1.19s

결론

LGTM — 추가 소견 없음. 머지 준비 완료.

## 추가 리뷰 — risk: polish retry coverage and refresh failure summary **커밋**: b34937e ### ✅ 소견 반영 확인 **소견 1 (pragma: no cover 제거)** _run_refresh_with_retry()의 except 블록에서 pragma: no cover 제거 완료. 테스트가 실제로 이 경로를 실행하므로 커버리지 리포트가 정확해집니다. ✅ **소견 2 ((+N more) 절삭 표시)** _refresh_order_state_for_kill_switch()에 적용 완료: summary = "; ".join(failures[:3]) if len(failures) > 3: summary = f"{summary} (+{len(failures) - 3} more)" raise RuntimeError(summary) 추가로 _cancel_pending_orders_for_kill_switch()에도 동일하게 적용 — 소견 범위를 넘어선 일관성 개선 ✅ **신규 테스트** test_refresh_order_state_failure_summary_includes_more_count: - 4개 마켓 전부 실패 → "(+1 more)" 말미 포함 검증 (match=r"\(\+1 more\)$") - "KR/KRX" 첫 번째 실패 항목 포함 검증 로컬 실행: 5 passed in 1.19s ✅ ### 결론 **LGTM** — 추가 소견 없음. 머지 준비 완료.
jihoson merged commit 87683a88b4 into feature/v3-session-policy-stream 2026-03-02 09:47:56 +09:00
jihoson deleted branch feature/issue-377-kill-switch-refresh-retry 2026-03-02 09:47:56 +09:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jihoson/The-Ouroboros#389