feat(bat-053): 그날-시작 KIS 인증정보 fetch + fail-safe 로드 (REQ-112~115)
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
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# BAT-053: Design
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### D1: `entry_allowed` 상태 위치 — SnapshotScheduler + 공유 callable
|
||||
|
||||
**결정**: `SnapshotScheduler`에 `entry_allowed: bool = True` (opt-in=False 기본 통과) 상태를 두고, IntentService에 `entry_allowed_fn: Callable[[], bool] | None` 게이트를 주입한다.
|
||||
|
||||
**근거**:
|
||||
- `entry_allowed`는 그날-시작 체크포인트(SnapshotScheduler.run_once())에서 설정되므로 소유자는 SnapshotScheduler가 자연스럽다.
|
||||
- IntentService는 이미 `warn_store`, `max_active_slots` 등 주입형 게이트 패턴을 사용한다. 동일 패턴으로 `entry_allowed_fn: Optional[Callable[[], bool]] = None`을 추가하면 기존 코드 변경 최소화.
|
||||
- `entry_allowed_fn=None`(기본)이면 게이트 완전 통과 → `credential_fetch_enabled=False`(기본)일 때 기존 동작 byte-for-byte 보존(회귀 0).
|
||||
- daemon.py에서 `scheduler.get_entry_allowed` 메서드를 `IntentService`에 주입하는 방식으로 조립.
|
||||
|
||||
**대안 검토**:
|
||||
- 별도 `DayStartState` 객체: 추가 클래스 생성 필요, SnapshotScheduler와 결합 불필요하게 증가.
|
||||
- IntentService 내부 setter: 의존 방향 역전(application이 application 내부를 조율하는 순환 구조) — 거부.
|
||||
|
||||
### D2: `credential_fetch_enabled` 플래그 전파 경로
|
||||
|
||||
**결정**: `DaemonInstanceConfig.credential_fetch_enabled: bool = False` → `DaemonConfig.from_config()`에서 `inst.credential_fetch_enabled`를 읽어 `DaemonConfig.credential_fetch_enabled: bool = False`에 설정. `SnapshotScheduler` 생성자에 `credential_fetch_enabled: bool = False` 인자 추가.
|
||||
|
||||
**근거**:
|
||||
- TOML → `DaemonInstanceConfig` → `DaemonConfig` → `SnapshotScheduler` 흐름이 기존 `safety_sell_enabled`, `watchdog_timeout_sec` 패턴과 동일. 일관성 유지.
|
||||
- `from_env()` 는 변경 없음(always `False`). `from_config()`만 TOML 값을 읽어 전파.
|
||||
|
||||
### D3: REQ-113 fetch 소스 우선순위 구현 위치
|
||||
|
||||
**결정**: 우선순위 로직은 `DaemonConfig.from_config()`에 두지 않고 **`SnapshotScheduler.run_once()`의 그날-시작 후크**에 둔다.
|
||||
|
||||
**근거**:
|
||||
- REQ-113 Notes: "`base_url`/timeout/allow_live는 env 유지". fetch는 app_key/app_secret/account_ref만 가져온다.
|
||||
- `DaemonConfig.from_config()`는 정적 구성 해결 단계 — 네트워크 I/O(fetch)를 여기에 두면 의존성 역전 위반(runtime이 portfolio-hub를 직접 호출).
|
||||
- `SnapshotScheduler.run_once()`는 이미 portfolio-hub를 호출하는 지점이고, `credential_fetch_enabled=True`일 때 추가 fetch가 자연스럽다.
|
||||
- break-glass(`TREX_KIS_BROKER_BREAK_GLASS=true`) 감지는 `SnapshotScheduler` 초기화 시 `os.environ`을 읽어 `_break_glass: bool` 플래그로 보관.
|
||||
- fetch 결과(`BrokerCredentialPayload`)는 `SnapshotScheduler._cached_credential`에 그날 고정 캐시.
|
||||
- **KISConfig 재조립 경로**: `SnapshotScheduler`에 `kis_config_callback: Optional[Callable[[BrokerCredentialPayload], None]]`를 주입. daemon.py에서 이 callback이 `KISBroker`를 재초기화하거나 config를 갱신하는 역할을 한다. 단, M3에서 중장기 인스턴스 추가 전까지는 단기 07:30 로드에서만 필요하고, BAT-053 범위에서는 `entry_allowed` 플래그 설정 + 알람까지만 구현. KISBroker 재조립은 M3 별도 batch.
|
||||
|
||||
**BAT-053 범위 명확화**:
|
||||
- REQ-113 AC-1~7 중 AC-2(fetch 결과로 KISConfig 구성)의 "KISBroker에 실제 적용" 부분은 M3 KISBroker 재조립 batch에 defer. 이번 batch에서는 fetch→캐시→entry_allowed 게이트 + 알람까지.
|
||||
- 단, AC-2 테스트는 TradingContext가 올바르게 반환됨을 stub으로 검증.
|
||||
|
||||
### D4: 그날 고정 캐시 설계 (REQ-115)
|
||||
|
||||
**결정**: `SnapshotScheduler`에 인스턴스 변수 추가:
|
||||
- `_cached_credential: Optional[BrokerCredentialPayload] = None`
|
||||
- `_credential_date: Optional[date] = None`
|
||||
- `_entry_allowed: bool = True` (opt-in=False면 항상 True = 차단 안 함)
|
||||
|
||||
`run_once()` 시 `credential_fetch_enabled=True`이면 `_maybe_fetch_credential()` 메서드 호출.
|
||||
`_maybe_fetch_credential()`:
|
||||
1. `_credential_date == today` → 그날 고정 캐시 반환(재fetch 안 함, REQ-115 AC-3)
|
||||
2. break-glass → env-only, 캐시 없음, `_entry_allowed=True`
|
||||
3. `fetch_broker_credential()` 호출 → 성공: 캐시 + `_entry_allowed=True`; 실패(3가지 조건): `_entry_allowed=False` + ERROR + CRITICAL 알람
|
||||
|
||||
`get_entry_allowed() -> bool` 메서드 노출 → daemon.py에서 IntentService.`entry_allowed_fn`에 주입.
|
||||
|
||||
### D5: 중장기 인스턴스 후크 위치 (REQ-114 Notes)
|
||||
|
||||
**결정**: BAT-053에서는 단기 07:30 KST 경로(SnapshotScheduler.run_once())만 구현. 중장기 인스턴스(dcy-027 리밸런싱 주기)의 동등 후크는 BAT-053 구현 후 그 위치를 확인하고 설계에 명시. 현재 코드상 중장기 후크는 SnapshotScheduler._maybe_tick()과 run_once()를 공유하므로 `credential_fetch_enabled=True`이면 동일 경로로 처리됨. dcy-027 추가 인스턴스가 별도 run_once() 트리거 주기를 가질 경우 그때 조정.
|
||||
|
||||
### D6: AlarmEmitter `DAY_START_LOAD_FAILED`
|
||||
|
||||
**결정**: AlarmEmitter 자체는 변경 없음(코드는 문자열 파라미터로 전달). `SnapshotScheduler`에서 `self._alarm_emitter.publish_alarm("DAY_START_LOAD_FAILED", ...)` 직접 호출. ALARM-INVENTORY.md 또는 별도 코드 상수 추가는 BAT-053 범위 외(dcy-033/REQ-100 패턴 재사용).
|
||||
|
||||
## Module Interface Summary
|
||||
|
||||
### `application/portfolio.py` 추가
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class BrokerCredentialPayload:
|
||||
account_ref: str
|
||||
app_key: str
|
||||
app_secret: str
|
||||
credential_version: Optional[int] = None
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TradingContext:
|
||||
broker: Optional[BrokerCredentialPayload]
|
||||
trading_enabled: bool
|
||||
|
||||
# PortfolioHubReadPort에 추가:
|
||||
def fetch_broker_credential(self) -> TradingContext: ...
|
||||
```
|
||||
|
||||
### `adapters/portfolio/hub_read.py` 변경
|
||||
- `whoami()` 내부 로직을 `_fetch_user_info() -> dict` 헬퍼로 추출
|
||||
- `whoami()` = `_fetch_user_info()`에서 `daemon_id` 필드 반환 (외부 동작 불변)
|
||||
- `fetch_broker_credential()` = `_fetch_user_info()`에서 `broker`, `trading_enabled` 파싱
|
||||
|
||||
### `adapters/portfolio/stub_read.py` 변경
|
||||
- `trading_context: Optional[TradingContext] = None` 생성자 인자 추가
|
||||
- `fetch_broker_credential()` stub 구현
|
||||
|
||||
### `runtime/config.py` 변경
|
||||
- `DaemonInstanceConfig.credential_fetch_enabled: bool = False` 추가 + `from_toml()` 파싱
|
||||
- `DaemonConfig.credential_fetch_enabled: bool = False` 추가
|
||||
- `DaemonConfig.from_config()`: `inst.credential_fetch_enabled` → `DaemonConfig.credential_fetch_enabled` 전파
|
||||
|
||||
### `application/snapshot_scheduler.py` 변경
|
||||
- 생성자: `credential_fetch_enabled: bool = False` 인자 추가
|
||||
- 인스턴스 변수: `_cached_credential`, `_credential_date`, `_entry_allowed`
|
||||
- `_maybe_fetch_credential()` 비공개 메서드 추가
|
||||
- `run_once()`: `credential_fetch_enabled=True`이면 `_maybe_fetch_credential()` 호출
|
||||
- `get_entry_allowed() -> bool` 노출
|
||||
|
||||
### `application/intents.py` 변경 (IntentService)
|
||||
- 생성자: `entry_allowed_fn: Optional[Callable[[], bool]] = None` 추가
|
||||
- `_admit_buy()`: `entry_allowed_fn is not None and not entry_allowed_fn()` → reject `day_start_load_failed`
|
||||
|
||||
### `runtime/daemon.py` 변경
|
||||
- `SnapshotScheduler` 생성 시 `credential_fetch_enabled=config.credential_fetch_enabled` 전달
|
||||
- `IntentService` 생성 시 `entry_allowed_fn=self.snapshot_scheduler.get_entry_allowed` 전달 → SnapshotScheduler가 먼저 생성되어야 함 (순서 조정 필요 확인)
|
||||
|
||||
## Layer Compliance
|
||||
|
||||
- `domain/` 변경 없음 — 도메인 순수성 불변
|
||||
- `application/portfolio.py`: 새 dataclass(frozen=True) — IO/네트워크 없음 (import sweep 통과)
|
||||
- `application/snapshot_scheduler.py`: portfolio 포트 호출(이미 포트 인터페이스) — 신규 import 없음
|
||||
- `application/intents.py`: callable 주입 — 추가 import 없음
|
||||
- `adapters/`: 구체 구현만 변경
|
||||
- `runtime/`: 조립 레이어 변경
|
||||
@@ -0,0 +1,91 @@
|
||||
# BAT-053: Implementation
|
||||
|
||||
## 변경 파일 목록
|
||||
|
||||
### 신규/확장 모듈
|
||||
|
||||
| 파일 | 변경 유형 | 관련 REQ |
|
||||
| --- | --- | --- |
|
||||
| `trex_core/application/portfolio.py` | 확장 | REQ-112 |
|
||||
| `trex_core/adapters/portfolio/hub_read.py` | 확장 | REQ-112 |
|
||||
| `trex_core/adapters/portfolio/stub_read.py` | 확장 | REQ-112 |
|
||||
| `trex_core/runtime/config.py` | 확장 | REQ-113 |
|
||||
| `trex_core/application/snapshot_scheduler.py` | 확장 | REQ-113/114/115 |
|
||||
| `trex_core/application/intents.py` | 확장 | REQ-114 |
|
||||
| `trex_core/runtime/daemon.py` | 확장 (조립) | REQ-113/114 |
|
||||
|
||||
### 신규 테스트
|
||||
|
||||
| 파일 | 관련 REQ |
|
||||
| --- | --- |
|
||||
| `tests/adapters/portfolio/test_hub_read.py` (확장) | REQ-112 AC-1~7 |
|
||||
| `tests/adapters/portfolio/test_stub_read.py` (확장) | REQ-112 AC-6 |
|
||||
| `tests/runtime/test_instance_config.py` (확장) | REQ-113 AC-1,7 |
|
||||
| `tests/application/test_snapshot_scheduler.py` (확장) | REQ-114 AC-1~9, REQ-115 AC-3/6 |
|
||||
|
||||
## REQ별 구현 요약
|
||||
|
||||
### REQ-112: BrokerCredentialPayload + TradingContext + 포트 메서드 + 어댑터
|
||||
|
||||
- `application/portfolio.py`:
|
||||
- `BrokerCredentialPayload(account_ref, app_key, app_secret, credential_version)` frozen dataclass 추가
|
||||
- `TradingContext(broker, trading_enabled)` frozen dataclass 추가
|
||||
- `PortfolioHubReadPort.fetch_broker_credential() -> TradingContext` 포트 메서드 추가
|
||||
- `adapters/portfolio/hub_read.py`:
|
||||
- `whoami()` 내부 로직을 `_fetch_user_info() -> dict` 헬퍼로 추출 — 외부 동작 불변(AC-5 회귀 보존)
|
||||
- `fetch_broker_credential()` 구현: broker null → TradingContext(broker=None), credential_payload null → TradingContext(broker=None), 성공 → BrokerCredentialPayload 반환
|
||||
- NFR-2: app_secret 평문 로그 없음 — 어댑터 내 로깅 코드에 app_secret 미포함
|
||||
- `adapters/portfolio/stub_read.py`:
|
||||
- `trading_context: Optional[TradingContext]`, `fetch_broker_raises: bool` 인자 추가
|
||||
- `fetch_broker_credential()` stub 구현 — 네트워크 없이 결정론적 반환/예외
|
||||
|
||||
### REQ-113: credential_fetch_enabled opt-in + break-glass
|
||||
|
||||
- `runtime/config.py`:
|
||||
- `DaemonInstanceConfig.credential_fetch_enabled: bool = False` 추가 (TOML opt-in, 기본 False)
|
||||
- `DaemonInstanceConfig.from_toml()`: `credential_fetch_enabled` 파싱 (부재 시 False)
|
||||
- `DaemonConfig.credential_fetch_enabled: bool = False` 추가
|
||||
- `DaemonConfig.from_config()`: `inst.credential_fetch_enabled` → `DaemonConfig` 전파
|
||||
- `DaemonConfig.from_env()`: 변경 없음 → 항상 False (AC-1 회귀 0)
|
||||
- `application/snapshot_scheduler.py`:
|
||||
- 생성자 `credential_fetch_enabled: bool = False` 인자 추가
|
||||
- 초기화 시 `TREX_KIS_BROKER_BREAK_GLASS` env 감지(break-glass 플래그)
|
||||
- break-glass: INFO 로그 후 env-only 경로(fetch 없이 entry_allowed=True)
|
||||
|
||||
### REQ-114: 그날-시작 로드 게이트 + fail-closed + 알람
|
||||
|
||||
- `application/snapshot_scheduler.py`:
|
||||
- `_entry_allowed: bool = True` 상태 추가
|
||||
- `_maybe_fetch_credential()`: fail-closed 3조건 처리 + CRITICAL 알람 `DAY_START_LOAD_FAILED` 발신
|
||||
- `run_once()` 선두: `credential_fetch_enabled=True`이면 `_maybe_fetch_credential()` 호출
|
||||
- `get_entry_allowed() -> bool` 노출
|
||||
- `application/intents.py`:
|
||||
- `IntentService` 생성자 `entry_allowed_fn: Optional[Callable[[], bool]] = None` 추가
|
||||
- `_admit_buy()` 선두: `entry_allowed_fn is not None and not entry_allowed_fn()` → reject `day_start_load_failed`
|
||||
- SellIntent(`_admit_sell`) 변경 없음 — 청산 경로 영향 없음 (AC-5)
|
||||
- `runtime/daemon.py`:
|
||||
- `SnapshotScheduler` 생성 후 `entry_allowed_fn = scheduler.get_entry_allowed`를 IntentService에 주입
|
||||
- `credential_fetch_enabled=False`(기본) → `entry_allowed_fn=None` → 게이트 비활성
|
||||
|
||||
### REQ-115: 그날 고정 캐시 + 장중 무중단
|
||||
|
||||
- `application/snapshot_scheduler.py`:
|
||||
- `_cached_credential: Optional[BrokerCredentialPayload]`, `_credential_date: Optional[date]` 추가
|
||||
- `_maybe_fetch_credential()`: `_credential_date == today` 시 재fetch 생략 (그날 고정 캐시, AC-3)
|
||||
- 성공 시 캐시 저장 + INFO 로그(account_ref/trading_enabled/credential_version; app_secret 미포함)
|
||||
- Redis 등 외부 저장소 기록 없음 — 메모리 한정 (AC-5)
|
||||
|
||||
## 설계 결정 반영 확인
|
||||
|
||||
- D1(entry_allowed 위치): SnapshotScheduler 내 + IntentService callable 주입 — 구현됨
|
||||
- D2(credential_fetch_enabled 전파): DaemonInstanceConfig → DaemonConfig → SnapshotScheduler — 구현됨
|
||||
- D3(우선순위 로직 위치): SnapshotScheduler._maybe_fetch_credential() — 구현됨
|
||||
- D4(그날 고정 캐시): `_credential_date` 비교로 동일 날짜 재fetch 방지 — 구현됨
|
||||
- D5(중장기 후크): BAT-053에서는 단기 07:30 경로(run_once())만 구현. 중장기는 동일 run_once() 경유로 처리됨 — 확인
|
||||
- D6(AlarmEmitter): publish_alarm("DAY_START_LOAD_FAILED", ...) 직접 호출 패턴 — 구현됨
|
||||
|
||||
## 주요 설계 메모
|
||||
|
||||
- `credential_fetch_enabled=False`(기본)일 때: `_maybe_fetch_credential()` 미호출 → `_entry_allowed=True` 고정 → IntentService `entry_allowed_fn=None` → 게이트 완전 비활성. 기존 동작 byte-for-byte 보존(회귀 0).
|
||||
- KISBroker 재조립(fetch 결과를 실제 KIS API에 적용)은 BAT-053 범위 외 — M3 별도 batch. 현재 `_cached_credential`이 메모리에 저장되어 있으며 다음 batch에서 KISBroker 재초기화에 활용.
|
||||
- REQ-115 AC-2(ledger_outbox 무손실): 기존 REQ-019 구현 속성 재확인. 별도 구현 불필요, 기존 테스트 그린(745 passed).
|
||||
@@ -0,0 +1,31 @@
|
||||
# BAT-053: 그날-시작 KIS 인증정보 fetch + fail-safe 로드
|
||||
|
||||
- Status: in-progress
|
||||
- Profile: standard
|
||||
- Discovery: dcy-036
|
||||
- Created: 2026-06-28
|
||||
|
||||
## Included REQ
|
||||
|
||||
- REQ-112: portfolio-hub broker credential read 포트 메서드 + HTTP 어댑터
|
||||
- REQ-113: KISConfig 소스 우선순위 — portfolio-hub fetch 1차 + env break-glass + opt-in
|
||||
- REQ-114: 그날-시작 로드 게이트 — fail-closed 신규 진입 차단 + 알람
|
||||
- REQ-115: 그날-시작 장중 무중단 보장 — 그날 고정 인증정보 캐시 + portfolio-hub 장애 비중단
|
||||
|
||||
## Excluded REQ
|
||||
|
||||
없음 — dcy-036 인증정보 fetch 4개 REQ 전체 포함.
|
||||
|
||||
## Documents
|
||||
|
||||
- [planning.md](./planning.md)
|
||||
- [design.md](./design.md)
|
||||
- [implementation.md](./implementation.md)
|
||||
- [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
|
||||
- Source: dcy-036 (그날-시작 KIS 인증정보 fetch + fail-safe 로드). REQ-112~115 모두 같은 discovery.
|
||||
- 사장님 지시: 스텁 기반으로 구현해 실 portfolio-hub 없이 테스트 가능하게.
|
||||
- 워크트리 격리 배치(origin/main 분기). 코드 변경은 이 워크트리 브랜치에 커밋.
|
||||
- AMQP wire 계약 변경 없음 — trex-orb-v1/trex-tsl-v1 파급 없음.
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.py` AC-1~7, `test_stub_read.py` AC-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.py` AC-1~7(추가)
|
||||
|
||||
### T3: REQ-114 + REQ-115 — 그날-시작 게이트 + 그날 고정 캐시
|
||||
- `application/snapshot_scheduler.py`:
|
||||
- `run_once()` 앞에 `credential_fetch_enabled=True` 경로에서 `fetch_broker_credential()` 호출
|
||||
- `entry_allowed: bool` in-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.py` AC-1~9 + REQ-115 AC-1~6
|
||||
- 테스트: `tests/application/test_execution.py` 또는 test_intent 계열 AC-4, AC-5
|
||||
|
||||
## Design Gate
|
||||
|
||||
**design.md 필요함(standard profile)**: 다음 판단이 필요.
|
||||
1. `entry_allowed` 상태를 어디에 보관할지: SnapshotScheduler 내부 vs 별도 `DayStartState` 객체 vs IntentService에 주입.
|
||||
2. IntentService에 `entry_allowed` 게이트를 어떻게 연결할지(생성자 주입 callable vs setter vs 공유 객체).
|
||||
3. `credential_fetch_enabled` 플래그가 False(기본)일 때 기존 경로 byte-for-byte 보존 검증 방법.
|
||||
4. 중장기 인스턴스(dcy-027)의 그날-시작 후크 위치 확인(현 코드에서 run_once() 위치 분석 필요).
|
||||
5. `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).
|
||||
@@ -60,3 +60,4 @@
|
||||
| BAT-050 | standard | release-candidate | REQ-102, REQ-103, REQ-104, REQ-107, REQ-108 | dcy-034 (core-A) | [bat-050](./bat-050_20260626_reconcile-warning-blocking-gate-core-a/) |
|
||||
| BAT-051 | standard | release-candidate | REQ-105, REQ-106 | dcy-034 (core-B) | [bat-051](./bat-051_20260626_sync-verify-cli-cold-start-core-b/) |
|
||||
| BAT-052 | standard | release-candidate | REQ-109, REQ-110, REQ-111 | dcy-035 | [bat-052](./bat-052_20260626_wac-realized-pnl-report/) |
|
||||
| BAT-053 | standard | in-progress | REQ-112, REQ-113, REQ-114, REQ-115 | dcy-036 | [bat-053](./bat-053_20260628_daystart-credential-fetch-failsafe/) |
|
||||
|
||||
Reference in New Issue
Block a user