# BAT-039 Implementation - Status: completed - Batch: BAT-039 ## Plan Summary - `seq_store.py` 신규(`SeqStore`/`RedisSeqStore`/`InMemorySeqStore`), paper 어댑터에 optional 주입, config/daemon wiring, 재시작 모사 회귀 테스트. ## Changed Files - `trex_core/adapters/broker/seq_store.py` (신규) — `SeqStore` Protocol, `RedisSeqStore`(INCR), `InMemorySeqStore` - `trex_core/adapters/broker/paper.py` — `SeqStore` import, `__init__(seq_store=None)`, `submit_order` 발번 분기 - `trex_core/runtime/config.py` — `PaperBrokerConfig.redis_url` + `TREX_PAPER_REDIS_URL` - `trex_core/runtime/daemon.py` — `_build_broker` paper 경로 `RedisSeqStore` wiring(key=`trex:broker:paper:{instance_key}:order-seq`) - `tests/adapters/broker/test_paper_seq_store.py` (신규, 5 테스트) ## Execution Log 1. `seq_store.py`: `next_seq()` 단일 메서드 포트. `RedisSeqStore` 는 `from_url(socket_connect_timeout=2)` + 원자 `INCR`(키 부재 시 1부터 → 첫 order_id `PAPER-000001`). `InMemorySeqStore` 는 lock 보호 카운터. 런타임 Redis 오류는 전파(silent in-memory 폴백 금지, 버그 재현 방지). 2. `paper.py`: `seq_store` 주입 시 `next_seq()`, 미주입 시 기존 `self._seq += 1`(byte-for-byte 유지). `_lock` 안에서 발번. 3. `config.py`: `PaperBrokerConfig.redis_url: str = ""`, `from_config` 에서 `TREX_PAPER_REDIS_URL` 읽기. 4. `daemon.py`: `config.paper.redis_url` 있으면 `RedisSeqStore` 생성·주입(instance_key 스코프 키). 5. 테스트 5종: 재시작 단조성, 매수→재시작→매도 correlation_id 비충돌, (버그 문서화) store 없을 때 충돌 재현, in-memory 폴백 시작값, fake redis INCR 단조·공유. ## Validation - `pytest tests/adapters/broker/test_paper_seq_store.py` — 5 passed - `pytest tests/` — **434 passed, 45 skipped** (BAT-038 429 → +5, 회귀 0) - `ruff check trex_core tests` — All checks passed - 폴백 회귀: `test_in_memory_fallback_starts_at_one` 으로 미주입 시 `PAPER-000001` 시작 확인(AC-1~3 기존 paper 테스트 회귀 없음). ## Remaining Risks - 없음. R-1(런타임 오류 silent 폴백)은 전파 설계로 제거, R-2(자리수 초과)는 `:06d` 최소 패딩으로 무해, R-3(다중 인스턴스 키 공유)은 instance_key 스코프로 분리. - 라이브 Redis 통합 스모크는 운영 배포 단계 사항(테스트는 fake redis_module + InMemorySeqStore 로 INCR/단조 계약 검증). 실 KIS 는 본 변경 무관(odno 고유).