docs(dcy-036): confirm-discovery — 그날-시작 KIS 인증정보 fetch + fail-safe 거래 로드 (교차발원 pf-hub dcy-010)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
2026-06-28 00:17:18 +09:00
co-authored by Claude Opus 4.8
parent 6da552bedb
commit 9312ac4401
2 changed files with 191 additions and 0 deletions
@@ -0,0 +1,190 @@
# 0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-036_20260628_daystart-credential-fetch-failsafe-trading-load
- 이슈명: 그날-시작 KIS 인증정보 fetch(portfolio-hub) + fail-safe 거래 로드 — 진실원천 부재 시 신규 거래 미개시, 장중 무중단 (교차 발원 portfolio-hub dcy-010)
- 작성 시각(KST): 2026-06-28
- 마지막 갱신 시각(KST): 2026-06-28
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음
# 1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 점수 파일: 미생성
- 관련 TODO/메모: M3 메인(중장기 trex-core 인스턴스). 교차 발원 = portfolio-hub `dcy-010`(daemon-scoped 브로커 인증정보 소유·전달). 선행 = trex-core `dcy-027`(중장기 슬롯 운영 프로파일), `dcy-034`(cold-start reconcile)
- 해석:
- portfolio-hub가 universe·전략·KIS 인증정보의 단일 진실원천이라는 결정(사장님)에 따라, trex-core가 KIS 인증정보를 env 직접주입 대신 **그날 시작 시점에 portfolio-hub `/v1/user_info`(daemon-scoped 단일 broker)에서 fetch**해 `KISConfig`를 구성하도록 소비자측을 정의한다.
- 핵심 판단: "그날-시작 로드를 신선도 게이트로 삼아 진실원천 부재 시 신규 거래를 개시하지 않되(fail-closed), 이미 시작된 그날의 전략 실행은 장중 portfolio-hub 장애에도 중단하지 않는다"는 fail-safe 의미론을 trex-core 런타임에 어떻게 구현·경계지을지.
# 2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
- 판정: 신규
- 근거
- 기존 trex-core는 KIS를 `TREX_KIS_BROKER_*` env로만 주입하며(`runtime/config.py`), portfolio-hub에서 인증정보를 받지 않는다. portfolio-hub 연동은 원장 쓰기(outbox)와 read(whoami/portfolios)만 존재.
- "그날-시작 fail-safe 로드 게이트"는 기존 어느 REQ도 정의한 바 없는 신규 런타임 동작이며, 외부 서비스(portfolio-hub) 의존을 매매 임계 경로 경계에 도입하는 구조 변경이라 독립 주기로 추적한다.
# 3. 문제점의 요약
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다.
- 현재 상태
- `KISConfig`는 `TREX_BROKER=kis`일 때 `TREX_KIS_BROKER_BASE_URL/APP_KEY/APP_SECRET/ACCOUNT_REF` 등 env에서만 구성된다(`trex_core/runtime/config.py:257`). 인스턴스별 계좌 분리는 프로세스별 env/.env 격리에만 의존.
- portfolio-hub read 어댑터(`trex_core/adapters/portfolio/hub_read.py`)가 이미 존재 — `whoami()`가 `GET /v1/user_info`를 호출해 `daemon_id`만 읽고, `get_portfolios(daemon_id)`로 universe(클라이언트측 daemon 필터)를 받는다. 인증 토큰(`TREX_PORTFOLIO_HUB_*`) 토대 존재.
- 일일 스냅샷 리프레시(`TREX_SNAPSHOT_SCHEDULE_KST`, 기본 07:30 KST; `SnapshotScheduleConfig`)가 존재 — "그날-시작" 로드의 자연스러운 후크 후보.
- 체결 원장은 trex-core 자기 PG `ledger_outbox`(REQ-019)에 즉시 durable 기록 후 portfolio-hub로 eventual drain → portfolio-hub 장애 시에도 거래내역 무손실(지연만).
- 기대 상태
- trex-core 인스턴스가 그날 시작 시 portfolio-hub에서 자기 daemon의 KIS 인증정보(+universe+전략+trading_enabled)를 받아 `KISConfig`를 구성하고 그날 거래를 개시한다.
- portfolio-hub가 그날-시작 시점에 부재/불가면 그날 신규 거래를 **개시하지 않는다(fail-closed)**.
- 장중 portfolio-hub 장애는 **이미 시작된 그날의 전략 실행(진입·청산 포함)을 중단시키지 않는다**. 그날 인증정보는 캐시로 운행, 원장은 outbox로 지연 흡수.
- 이해관계자
- 사장님(fail-safe 정책·계좌 분리 방향·결재), 팀장(Discovery·거버넌스), trex-core-dev(소비자 어댑터·로드 게이트 구현), 운영자(그날-시작 로드 실패 대응)
- 제약사항
- 라이브 단기 인스턴스(M1/M2)에 회귀 금지 — portfolio-hub 의존 도입이 기존 매매를 깨지 않아야 한다(break-glass 경로 필요).
- portfolio-hub dcy-010(daemon-scoped 단일 broker `/v1/user_info`)이 선행 의존 — 미배포 시 이 기능 비활성.
- 최민감 시크릿(app_secret)을 메모리/캐시에서 다루므로 로깅·노출 주의.
## 주요 사용자 시나리오
- 아침 07:30 스냅샷에서 중장기 인스턴스가 portfolio-hub fetch에 성공 → 자기 계좌로 그날 거래 개시. 11시 portfolio-hub가 잠시 다운되어도 그날 진입/청산은 캐시된 인증정보로 계속, 원장은 복구 후 drain.
- 아침 07:30에 portfolio-hub가 다운 → 인스턴스는 그날 신규 거래를 개시하지 않고 운영자에게 알람. portfolio-hub 복구 후 다음 로드 사이클에 정상화.
- 운영자가 비상 시 env로 KIS 인증정보를 직접 주입(break-glass)해 portfolio-hub 없이 인스턴스를 띄운다.
# 4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목.
- (C1) **KISConfig 소스 확장** — env-only → "그날-시작 portfolio-hub fetch"를 1차 소스로, env를 break-glass 오버라이드로. (`hub_read` 어댑터에 broker 인증정보 read 추가)
- (C2) **그날-시작 로드 게이트(fail-closed 진입)** — 로드 시점 portfolio-hub 부재 시 그날 신규 거래(진입) 미개시. 로드 성공분으로 그날 운행.
- (C3) **장중 무중단 보장** — 그날 시작 후 portfolio-hub 장애가 이미 로드된 그날 실행(진입·청산)을 중단시키지 않음(인증정보 캐시 + 원장 outbox 의존).
## 영향 범위
- 코드 영향:
- `trex_core/adapters/portfolio/hub_read.py`(broker 인증정보 read 추가), `trex_core/runtime/config.py`(KISConfig 소스/우선순위), 그날-시작 로드 후크(`application/snapshot_scheduler.py` 또는 daemon 부트스트랩), 로드 게이트가 진입 admission(IntentService)와 맞물리는 지점
- 캐시 저장소(기존 `TREX_KIS_REDIS_URL` 토큰 캐시 패턴 재사용 검토)
- 문서 영향:
- `docs/srs/`(신규 REQ), `docs/discovery/index.md`(본 문서 등록), `docs/runtime-flows.md`(그날-시작 로드 흐름 추가 검토)
- 교차: portfolio-hub `dcy-010` 계약 소비측 정합
- 운영 영향:
- 그날-시작 로드 실패 시 운영 절차(알람·수동 개입·break-glass). 인스턴스별 service token(daemon_id 바인딩) 발급·배치.
# 5. 요구사항 목록
## 기능 요구사항
- FR-1: trex-core가 그날-시작 시점에 service token으로 portfolio-hub `/v1/user_info`(daemon-scoped 단일 broker)를 호출해 KIS 인증정보(account_ref + app_key/app_secret + credential_version)를 받아 `KISConfig`를 구성한다. **그날-시작 체크포인트는 horizon별로 분리(결정 2026-06-28)**: 단기 = 07:30 KST 스냅샷 1회, 중장기 = dcy-027 운영 프로파일 기반 리밸런싱 주기. 그날 로드된 인증정보는 **그날 고정**이며 장중 portfolio-hub 복구 시에도 재fetch하지 않는다(결정론).
- FR-2: 그날-시작 로드 시점에 portfolio-hub가 불가(네트워크/비2xx/인증정보 부재/`trading_enabled=false`)면 그날 **신규 진입을 개시하지 않는다(fail-closed)**. 사유를 로깅하고 운영자에게 알람한다.
- FR-3: 그날 시작 후 발생한 portfolio-hub 장애는 이미 로드된 그날 실행을 중단시키지 않는다. 그날 인증정보는 캐시로 유지되어 진입·청산이 계속된다.
- FR-4: 평시에는 **portfolio-hub fetch가 1차 소스**이고, env(`TREX_KIS_BROKER_*`)는 **명시적 break-glass 오버라이드**다(결정 2026-06-28). 즉 fetch를 우선 시도하되, 운영자가 의도적으로 env break-glass를 활성화하면 portfolio-hub 없이도 인스턴스를 띄울 수 있다. 단기 인스턴스(M1/M2)는 회귀 차단을 위해 **당분간 fetch opt-in**(미설정 시 기존 env-only 경로 보존, NFR-1).
## 비기능 요구사항
- NFR-1 (회귀안전): portfolio-hub fetch 미설정/비활성 시 기존 env-only 동작이 byte-for-byte 보존된다(M1/M2 단기 인스턴스 무회귀).
- NFR-2 (보안): 캐시·로그에 app_secret 평문이 남지 않는다(마스킹). 전송은 내부망/TLS·service-token.
- NFR-3 (관측성): 그날-시작 로드 성공/실패/캐시운행 상태가 로그·카운터·알람으로 가시화된다.
## 범위 경계
- In Scope
- trex-core 소비자 어댑터(인증정보 read) + 그날-시작 로드 게이트(fail-closed 진입) + 장중 무중단 + break-glass.
- Out of Scope
- portfolio-hub 측 daemon-scoped 인증정보 소유·전달(= dcy-010, 선행 의존).
- universe·전략 fetch 자체의 재설계(현 get_portfolios 경로 재사용; 본 문서는 인증정보 로드와 fail-safe 게이트에 집중).
- {{DECIDE_BOOTSTRAP_UNIFY:universe+인증정보+trading_enabled를 단일 부트스트랩 호출로 통합할지 — dcy-010 OQ-3와 공동 결정}}
# 6. 리스크/가정 목록
## 리스크
- R-1 (High): portfolio-hub 의존 도입이 라이브 단기 인스턴스(M1/M2) 매매를 깨뜨림(회귀).
- 완화 방안: fetch는 opt-in. 미설정 시 env-only 경로 보존(NFR-1). break-glass 오버라이드 상시 유지. 단기 인스턴스는 당분간 env 유지 가능.
- R-2 (High): "그날-시작"의 정의가 horizon별로 달라(단기=매일 07:30 / 중장기=리밸런싱 주기) 로드 게이트 의미가 모호.
- 완화 방안: OQ-1로 horizon별 체크포인트 정의를 confirm 전 확정. 단기는 07:30 스냅샷, 중장기는 프로파일 기반 주기로 분리.
- R-3 (Medium): app_secret을 메모리/캐시에서 다뤄 노출 표면 증가.
- 완화 방안: 마스킹 로깅, 캐시 TTL/스코프 한정, Redis 사용 시 키 스코프(daemon/앱키) 격리.
## 가정
- A-1: portfolio-hub = universe·전략·KIS 인증정보의 단일 진실원천. (사장님 confirmed)
- A-2: 신선도 게이트 = **그날-시작 로드 시점**. 장중 portfolio-hub 장애는 그날 실행을 중단시키지 않는다("그날의 전략 실행은 그대로 유지"). (사장님 confirmed 2026-06-28)
- A-3: portfolio-hub `dcy-010`(daemon-scoped 단일 broker `/v1/user_info`)이 선행 제공된다.
- A-4: env break-glass 오버라이드는 유지된다(비상 경로).
- A-5: 그날 거래내역은 trex-core `ledger_outbox`로 무손실 흡수되므로 장중 portfolio-hub 장애가 원장 유실로 이어지지 않는다.
## 오픈 질문
- OQ-1: "그날-시작" 로드 체크포인트 정의 (horizon별).
- 상태: **Answered (2026-06-28)**
- 결정: 단기 = 07:30 KST 스냅샷 1회, 중장기 = dcy-027 운영 프로파일 기반 리밸런싱 주기로 분리. 본문 FR-1에 반영.
- 반영 위치: §5 FR-1.
- OQ-2: 장중 portfolio-hub 복구 시 재fetch vs 그날 고정.
- 상태: **Answered (2026-06-28)**
- 결정: **그날 고정(결정론)** — 장중 재fetch 안 함. 본문 FR-1에 반영.
- 반영 위치: §5 FR-1.
- OQ-3: break-glass(env)와 fetch 우선순위.
- 상태: **Answered (2026-06-28)**
- 결정: **fetch 1차, env는 명시적 break-glass 오버라이드**(평시 fetch 우선). 단기는 fetch opt-in으로 회귀 차단. 본문 FR-4에 반영.
- 반영 위치: §5 FR-4, NFR-1.
- OQ-4: 그날-시작 로드 실패 알람 경로 — trex-notifier(dcy-033) 연동 형태와 등급(CRITICAL?).
- 상태: Deferred
- 처리 방안: dcy-033 알람 envelope 계약 재사용. REQ 단계에서 구체화.
- 종료 조건: REQ 작성 시 알람 등급·채널 확정.
# 7. 초기 성공 기준
- S-1: portfolio-hub fetch 성공 시 그날 거래가 fetch된 계좌 인증정보로 개시됨을 검증할 수 있다.
- S-2: 그날-시작 시점 portfolio-hub 불가 시 신규 진입이 개시되지 않고(차단) 알람이 발생함이 테스트로 확인된다.
- S-3: 그날 시작 후 portfolio-hub를 내려도 그날 진입/청산이 캐시 인증정보로 계속되고 원장이 복구 후 drain됨이 검증된다.
- S-4: fetch 미설정 시 기존 env-only 동작이 회귀 없이 보존됨이 테스트로 확인된다.
## 측정 방식 및 데이터 출처
- S-1 측정 방식: fetch 모의 + KISConfig 구성 결과 검증 / 데이터 출처: 통합테스트
- S-2 측정 방식: 로드 시점 portfolio-hub 불가 주입 → 진입 admission 차단 + 알람 카운터 / 데이터 출처: 통합테스트, 로그/카운터
- S-3 측정 방식: 그날 시작 후 portfolio-hub 다운 주입 → 청산 경로 동작 + outbox drain 확인 / 데이터 출처: 통합테스트, ledger_outbox 행
- S-4 측정 방식: fetch 미설정 환경에서 기존 테스트 그린 / 데이터 출처: 기존 회귀 스위트
# 8. REQ로 넘기기 전 확인 체크
- [x] 범위(In Scope/Out of Scope) 확정
- 기준: 부트스트랩 통합(dcy-010 OQ-3 공동)은 Out of Scope Deferred 처리
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-4 측정 가능성 검토 완료
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1(회귀 — fetch opt-in·break-glass, trex-core-dev)·R-2(체크포인트 horizon별 정의 확정) 완화안 확정
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1·OQ-2·OQ-3 Answered, OQ-4 Deferred 처리 완료
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: 사장님+팀장 confirm-discovery (DECIDE/CONFIRM 전건 해소)
# 9. 파일 처리 결과
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-036_20260628_daystart-credential-fetch-failsafe-trading-load.md
- 참조 문서:
- portfolio-hub docs/discovery/dcy-010_20260627_daemon-scoped-broker-credential-provisioning.md (교차 발원 — 인증정보 소유·전달)
- docs/discovery/dcy-027_20260612_long-term-strategy-slot-operation.md (중장기 운영 프로파일)
- docs/discovery/dcy-034_20260625_slot-account-blocking-reconcile-sync-coldstart.md (cold-start 동기화)
- trex_core/adapters/portfolio/hub_read.py / trex_core/runtime/config.py (현재 구현)
# 10. 사용자 결정 필요 항목 요약
## DECIDE
- ~~그날-시작 체크포인트 (OQ-1)~~ → **RESOLVED 2026-06-28: 단기 07:30 스냅샷 / 중장기 dcy-027 프로파일 주기**
- ~~장중 재fetch (OQ-2)~~ → **RESOLVED 2026-06-28: 그날 고정(결정론)**
- ~~break-glass 우선순위 (OQ-3)~~ → **RESOLVED 2026-06-28: fetch 1차, env 명시적 break-glass**
## CONFIRM
- ~~fail-safe 모델 (A-2)~~ → **RESOLVED: 그날-시작 fail-closed 진입 + 장중 무중단 confirmed (사장님)**
- ~~단기 fetch opt-in~~ → **RESOLVED 2026-06-28: 단기 인스턴스 당분간 env 유지(fetch opt-in)로 회귀 차단 (FR-4/NFR-1)**
## DATA
- {{DATA_SERVICE_TOKEN_PROVISION:인스턴스별 service token(daemon_id 바인딩) 발급·배치 경로 — REQ/ops 단계에서 구체화(Deferred)}}
# 11. Discovery Freeze
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다.
## 섹션 참조
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: REQ Drafting 진행 가능
- Handoff Rationale: confirm-discovery 완료(2026-06-28). DECIDE 3건(OQ-1 horizon별 체크포인트·OQ-2 그날 고정·OQ-3 fetch 1차+env break-glass) 전건 Answered, CONFIRM 2건(fail-safe 모델·단기 opt-in) 해소, OQ-4(알람) Deferred. 잔여 Open 0건. **단, REQ 인계·구현은 선행 의존 portfolio-hub dcy-010(daemon-scoped 단일 broker /v1/user_info) 진척에 맞춰 조율.**
- Ready for REQ Drafting: true
- Confirmed By: 팀장(Claude)
- Confirmed At (KST): 2026-06-28
+1
View File
@@ -40,3 +40,4 @@
| dcy-032_20260621_fill-missing-price-cost-basis-backfill | 20260621 | 체결가 누락(브로커 평균가 0/None) → 슬롯 평단·원장 cost basis 0 오염(무경고·영구) → 지정가 백필 + 로깅 + 알람 검토 | [Discovery](./dcy-032_20260621_fill-missing-price-cost-basis-backfill.md) | confirmed |
| dcy-033_20260621_suite-alarm-delivery-notifier | 20260621 | 제품군 알람 전달 체계 — 모듈 발신 + 중앙 notifier(trex-notifier) → 텔레그램(P1 아웃바운드). envelope 계약+전송(trex.alerts AMQP 확정)+등급/상관억제/하트비트 | [Discovery](./dcy-033_20260621_suite-alarm-delivery-notifier.md) | confirmed |
| dcy-034_20260625_slot-account-blocking-reconcile-sync-coldstart | 20260625 | 슬롯↔실계좌 차단형 reconcile + sync/verify 명령 — 중장기 cold-start(기존 보유 onboarding). B/C 불일치→슬롯 차단(durable warning) + pause∨warning 게이트 + 운영자 sync/verify (M3) | [Discovery](./dcy-034_20260625_slot-account-blocking-reconcile-sync-coldstart.md) | confirmed |
| dcy-036_20260628_daystart-credential-fetch-failsafe-trading-load | 20260628 | 그날-시작 KIS 인증정보 fetch(portfolio-hub) + fail-safe 거래 로드 — 진실원천 부재 시 신규 거래 미개시, 장중 무중단 (교차 발원 portfolio-hub dcy-010) | [Discovery](./dcy-036_20260628_daystart-credential-fetch-failsafe-trading-load.md) | confirmed |