REQ-112: BrokerCredentialPayload/TradingContext 데이터클래스 + PortfolioHubReadPort.fetch_broker_credential() 포트 메서드 + hub_read 어댑터(_fetch_user_info 헬퍼 추출) + stub_read 결정론 stub REQ-113: DaemonInstanceConfig/DaemonConfig.credential_fetch_enabled opt-in(기본 False) + TOML 파싱 + break-glass(TREX_KIS_BROKER_BREAK_GLASS) 감지 REQ-114: SnapshotScheduler._maybe_fetch_credential() — fail-closed 3조건(unavailable/broker-null/trading_disabled) + CRITICAL 알람 DAY_START_LOAD_FAILED + IntentService entry_allowed_fn 게이트(BuyIntent 차단, SellIntent 통과) REQ-115: 그날 고정 캐시(_cached_credential/_credential_date) — 동일 날짜 재fetch 방지 + app_secret 메모리 한정(Redis 미기록) 스텁 기반 구현: 실 portfolio-hub/네트워크 없이 AC 전부 단위 테스트. 회귀: credential_fetch_enabled=False(기본) 경로 byte-for-byte 보존. 745 passed, 47 skipped (0 failed). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
4.0 KiB
4.0 KiB
BAT-053: Planning
Scope Summary
그날-시작 시 portfolio-hub /v1/user_info에서 KIS 인증정보를 fetch하고, 실패 시 fail-closed로 신규 진입을 차단하며, 성공 시 그날 캐시로 KIS API 장중 연속성을 보장한다.
4개 REQ 모두 같은 delivery 단위:
- REQ-112: 트랜스포트 계층 (포트 인터페이스 + HTTP 어댑터 + stub)
- REQ-113: 설정 계층 (TOML opt-in + fetch/break-glass/env-only 우선순위)
- REQ-114: 그날-시작 로드 게이트 (fail-closed + 알람 + BuyIntent 차단)
- REQ-115: 그날 고정 캐시 + 장중 무중단 NFR
Task Breakdown
T1: REQ-112 — 포트/어댑터 계층
application/portfolio.py:BrokerCredentialPayload,TradingContext데이터클래스 +PortfolioHubReadPort.fetch_broker_credential()포트 메서드 추가adapters/portfolio/hub_read.py:_fetch_user_info()헬퍼 추출 +fetch_broker_credential()구현adapters/portfolio/stub_read.py:fetch_broker_credential()stub 추가- 테스트:
tests/adapters/portfolio/test_hub_read.pyAC-1~7,test_stub_read.pyAC-6
T2: REQ-113 — 설정 계층
runtime/config.py:DaemonInstanceConfig.credential_fetch_enabled: bool = False+from_toml()파싱DaemonConfig.from_config()또는 new helper: fetch/break-glass/env-only 우선순위 로직- 테스트:
tests/runtime/test_instance_config.pyAC-1~7(추가)
T3: REQ-114 + REQ-115 — 그날-시작 게이트 + 그날 고정 캐시
application/snapshot_scheduler.py:run_once()앞에credential_fetch_enabled=True경로에서fetch_broker_credential()호출entry_allowed: boolin-process 상태 +_cached_credential: BrokerCredentialPayload | None+_credential_date: date | None- fail-closed 3 조건(PortfolioHubUnavailableError / broker None / trading_enabled False) → ERROR 로그 + CRITICAL 알람 DAY_START_LOAD_FAILED
- 그날 고정 — 장중 재fetch 방지
application/alarm_emitter.py:DAY_START_LOAD_FAILED알람 코드 추가(기존 패턴 재사용 — 실제로 코드 자체는 문자열이므로 사용처 추가)application/intents.py(IntentService):entry_allowed게이트를 BuyIntent admission에 추가- 테스트:
tests/application/test_snapshot_scheduler.pyAC-19 + REQ-115 AC-16 - 테스트:
tests/application/test_execution.py또는 test_intent 계열 AC-4, AC-5
Design Gate
design.md 필요함(standard profile): 다음 판단이 필요.
entry_allowed상태를 어디에 보관할지: SnapshotScheduler 내부 vs 별도DayStartState객체 vs IntentService에 주입.- IntentService에
entry_allowed게이트를 어떻게 연결할지(생성자 주입 callable vs setter vs 공유 객체). credential_fetch_enabled플래그가 False(기본)일 때 기존 경로 byte-for-byte 보존 검증 방법.- 중장기 인스턴스(dcy-027)의 그날-시작 후크 위치 확인(현 코드에서 run_once() 위치 분석 필요).
fetch_broker_credential()반환값이DaemonConfig.from_config()에서KISConfig재조립으로 이어지는 흐름 설계.
Risks
- R1: IntentService에
entry_allowed게이트를 연결할 때 기존 pass-through 테스트 회귀 가능성 →credential_fetch_enabled=False기본 경로에서 게이트를 비활성화하면 회귀 방지 - R2:
app_secret로그 노출 — 마스킹 로직 누락 주의 - R3:
stub_read.py의 stub이 포트 인터페이스와 불일치 → pytest import sweep으로 조기 발견 - R4: REQ-113의
DaemonConfig.from_config()변경이 기존test_instance_config.py회귀 유발 가능 → AC-1(opt-in off → env-only, fetch 미호출) 케이스가 핵심 회귀 가드
Dependencies
- portfolio-hub REQ-049 (dcy-010):
/v1/user_info확장 계약 — 머지 완료(미배포). 스텁 기반으로 실 portfolio-hub 없이 구현. - REQ-099/100 (dcy-033, BAT-049): AlarmEmitter
trex.alerts계약 — 이미 구현됨. - REQ-019: ledger_outbox 무손실 — 기존 구현 재확인(REQ-115 AC-2).