feat(intents): dcy-011 buy intent portfolio_id 필수 계약 + ledger skip 가시화 (BAT-015)
dcy-011 전 주기 delivery. issue #6(buy 식별자 정정) + issue #11(ledger silent skip) 흡수. 거버넌스: 기존 Implemented REQ는 change-req, 신규 기능만 신규 REQ(OQ-3). - REQ-007 (change-req): buy admission portfolio_id 필수화 + SnapshotStore symbol resolve + unknown_portfolio 거부 (hard cutover, OQ-1/OQ-2) - REQ-021 (change-req): buy 메시지 payload symbol 제거 - REQ-018 (change-req): _record_ledger skip 가시화 (WARN + ledger_skipped 카운터) - REQ-055 (신규): closed↔ledger_outbox anti-join 운영 점검 find_ledger_gap_slots 코드: intents.py(BuyIntent portfolio_id 필수 + resolve_symbol 주입 + _admit_buy reject 분기), messaging_handlers.py, runtime/daemon.py(_resolve_symbol 배선), execution.py(skip WARN 로그), adapters/persistence/ops_checks.py(신규 anti-join). 검증: 단위 282 passed/32 skipped, 인프라 게이트(PG+RabbitMQ) 314 passed, ruff clean. BAT-015(standard) release-candidate. srs/index Next ID → REQ-056. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# BAT-015 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-015
|
||||
- Profile: standard
|
||||
|
||||
## 설계 목표
|
||||
- buy intent 식별자 계약을 정식 식별자(portfolio_id=asset_id) 기준으로 hard cutover 하면서, symbol 입력 제거에 따른 다수 호출부 회귀를 통제한다.
|
||||
- issue #11 의 ledger silent skip 을 무성 → 가시화로 전환하되 전이 동작과 outbox 스키마는 불변으로 유지한다(OQ-4).
|
||||
|
||||
## 1. buy admission 계약 (REQ-007)
|
||||
|
||||
### BuyIntent 구조
|
||||
- `portfolio_id: str` 를 필수 위치 인자로 승격(intent_id, trading_date 다음). `symbol: Optional[str] = None` 으로 강등 — 클라이언트 입력이 아니라 admission 이 universe 에서 resolve 해 슬롯에 싣는 값.
|
||||
- 나머지 필드(qty/allocated_capital/entry_price/strategy_id/thresholds/extra)는 불변.
|
||||
|
||||
### symbol resolve 주입 경계
|
||||
- 결정: `IntentService(resolve_symbol=callable|None)` 콜백 주입(SnapshotStore 직접 의존 대신).
|
||||
- 근거: (a) application 계층이 portfolio.SnapshotStore 에 직접 결합되지 않아 테스트 더블 주입이 쉽다. (b) `resolve_symbol=None` 폴백을 두면 symbol resolve 가 필요 없는 기존 단위 테스트는 portfolio_id 추가만으로 무회귀(R-1 통제). 프로덕션 daemon 은 항상 `_resolve_symbol`(SnapshotStore.get) 주입.
|
||||
- `_resolve_symbol(portfolio_id) -> Optional[str]`. 부재 시 falsy.
|
||||
|
||||
### `_admit_buy` reject 분기 (FR-1/FR-2/FR-3)
|
||||
1. `not intent.portfolio_id` → `reject(portfolio_id_required)` (FR-1, 근본 원인 제거).
|
||||
2. resolver 주입됨: `symbol = resolve_symbol(portfolio_id)`; falsy 면 `reject(unknown_portfolio)` (FR-3, 단일 reject 정책 OQ-1 — symbol fallback 없음).
|
||||
3. resolver 미주입 + `intent.symbol` 도 falsy → `reject(unknown_portfolio)` (호출 대상 미상).
|
||||
4. resolve 성공 시 기존 idle-slot 배정·`transit_slot(IDLE→PENDING_BUY, symbol=symbol, portfolio_id=...)` 흐름 유지. extra 에도 resolved symbol 기록.
|
||||
|
||||
## 2. buy 메시지 payload 계약 (REQ-021)
|
||||
- buy 핸들러는 payload 에서 `symbol` 을 읽지 않는다. `portfolio_id = _opt_str(body.get("portfolio_id")) or ""` 로 매핑(빈 문자열 → admission 의 portfolio_id_required 로 일관 거부).
|
||||
- symbol 은 핸들러에서 BuyIntent 에 싣지 않음(서버 resolve 가 admission 책임).
|
||||
|
||||
## 3. daemon 배선 (REQ-007)
|
||||
- `runtime/daemon.py::_resolve_symbol(self, portfolio_id)` → `asset.symbol if (asset := self.snapshot_store.get(portfolio_id)) else None`.
|
||||
- `IntentService(..., resolve_symbol=self._resolve_symbol)` 주입.
|
||||
|
||||
## 4. ledger skip 가시화 (REQ-018, issue #11)
|
||||
- `_record_ledger` 두 조기 반환 경로 모두에서 `r.ledger_skipped += 1` 후 `_log.warning(...)`:
|
||||
- portfolio_id 누락: `reason=missing_portfolio_id` (slot_id/symbol/side).
|
||||
- execution_id 누락: `reason=missing_execution_id` (slot_id/portfolio_id/side, 브로커 계약 회귀 방어 케이스).
|
||||
- 슬롯 전이는 그대로(관측만 추가, OQ-4). outbox 마커 행/스키마 변경 없음.
|
||||
- `execution.py` 에 `import logging` + `_log = logging.getLogger(__name__)` 추가.
|
||||
|
||||
## 5. 운영 점검 쿼리 (REQ-055, FR-5)
|
||||
- `adapters/persistence/ops_checks.py::find_ledger_gap_slots(dsn, *, trading_date=None, psycopg_module=None)` — 읽기 전용.
|
||||
- anti-join SQL: `daily_slot s WHERE s.status='closed' AND (td IS NULL OR trading_date=td) AND (portfolio_id IS NULL OR NOT EXISTS(SELECT 1 FROM ledger_outbox o WHERE o.asset_id=s.portfolio_id))`.
|
||||
- 반환: `slot_id/trading_date/symbol/portfolio_id/broker_order_id` dict 리스트. `daily_slot` durable → outbox 마커 없이 영구 탐지(OQ-4 위임).
|
||||
- 스키마 변경 없음. 알람/주기 실행 배선은 운영 절차(Out of Scope).
|
||||
|
||||
## 책임 분리
|
||||
- 런타임 관측(REQ-018, WARN+카운터): "지금 skip 났다"를 즉시 노출.
|
||||
- 사후 탐지(REQ-055, anti-join): durable 슬롯 기준으로 누락을 영구·집계 탐지.
|
||||
- 둘 다 outbox 마커 행 없이 성립 → outbox 전송 큐 의미 오염 회피(OQ-4 근거).
|
||||
|
||||
## 하위호환 / cutover (NFR-3)
|
||||
- 호환 창구 없음(OQ-2). symbol 기반 호출은 portfolio_id 누락 시 명확히 reject.
|
||||
- 호출부 전환 전략:
|
||||
- 비-핸들러 intent/동시성/repository 테스트: BuyIntent 에 portfolio_id 추가(resolver None → intent.symbol 폴백).
|
||||
- 핸들러/RabbitMQ e2e 테스트: resolve_symbol 람다 + payload portfolio_id 동시 전환.
|
||||
- ledger skip 테스트: admission 우회(`transit_slot(portfolio_id=None)`)로 skip 전제 재구성 + caplog 검증.
|
||||
|
||||
## 문서 (FR-6)
|
||||
- `docs/interface-contract.md`: buy payload portfolio_id 필수 + symbol 제거/서버 resolve.
|
||||
- `docs/runtime-flows.md`: buy admission step 에 portfolio_id 필수 + symbol resolve + unknown_portfolio + ledger skip 가시화 단락.
|
||||
@@ -0,0 +1,40 @@
|
||||
# BAT-015 Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-015
|
||||
|
||||
## 구현 요약
|
||||
- dcy-011 buy intent 계약 hard cutover + ledger skip 가시화 + 운영 점검 쿼리를 단일 standard batch 로 구현. issue #6/#11 흡수.
|
||||
|
||||
## 변경 내역
|
||||
|
||||
### REQ-007 — buy admission portfolio_id 필수 + symbol resolve
|
||||
- `trex_core/application/intents.py`:
|
||||
- `BuyIntent`: `portfolio_id: str` 를 required 위치 인자로 승격, `symbol: Optional[str] = None` 로 강등(서버 resolve 결과를 담는 필드).
|
||||
- `IntentService.__init__(..., resolve_symbol=None)` — `callable(portfolio_id) -> Optional[str]` 주입점.
|
||||
- `_admit_buy`: ① `not portfolio_id` → `reject(portfolio_id_required)`; ② resolver 주입 시 `symbol = resolve_symbol(portfolio_id)`, falsy 면 `reject(unknown_portfolio)`; ③ resolver 미주입 + symbol falsy → `reject(unknown_portfolio)`; 이후 `transit_slot(symbol=symbol, portfolio_id=..., extra={..., "symbol": symbol})`.
|
||||
- `trex_core/runtime/daemon.py`: `_resolve_symbol(self, portfolio_id)` → `asset.symbol if (asset := self.snapshot_store.get(portfolio_id)) else None`; `IntentService(..., resolve_symbol=self._resolve_symbol)` 배선.
|
||||
|
||||
### REQ-021 — buy 메시지 payload symbol 제거
|
||||
- `trex_core/application/messaging_handlers.py`: buy 핸들러가 `symbol` 을 더 이상 읽지 않음; `portfolio_id=_opt_str(body.get("portfolio_id")) or ""` 매핑.
|
||||
|
||||
### REQ-018 — ledger skip 가시화 (issue #11)
|
||||
- `trex_core/application/execution.py`: `import logging` + `_log = logging.getLogger(__name__)`; `_record_ledger` 두 skip 경로(portfolio_id 누락 `reason=missing_portfolio_id`, execution_id 누락 `reason=missing_execution_id`)에서 `r.ledger_skipped += 1` + `_log.warning(...)`. 슬롯 전이 동작 불변, outbox 스키마 무변경(OQ-4).
|
||||
|
||||
### REQ-055 — closed↔outbox anti-join 운영 점검 (신규)
|
||||
- `trex_core/adapters/persistence/ops_checks.py`(신규): `find_ledger_gap_slots(dsn, *, trading_date=None, psycopg_module=None)` 읽기 전용. `status='closed'` 슬롯 중 `portfolio_id IS NULL` 또는 대응 `ledger_outbox` 행 부재(anti-join) 식별 → `slot_id/trading_date/symbol/portfolio_id/broker_order_id` dict 리스트. 스키마 변경 없음.
|
||||
|
||||
### FR-6 — 문서 정합
|
||||
- `docs/interface-contract.md`: buy payload `portfolio_id` 필수 + `symbol` 제거(서버 resolve). Last Updated → BAT-015.
|
||||
- `docs/runtime-flows.md`: buy admission step 에 portfolio_id 필수 + symbol resolve + unknown_portfolio + ledger skip 가시화 단락. Last Updated → BAT-015.
|
||||
|
||||
## 테스트 마이그레이션 (hard cutover, NFR-3)
|
||||
- 비-핸들러(resolver None → intent.symbol 폴백, portfolio_id 추가만): `test_execution.py`, `test_intent_concurrency.py`, `test_messaging_snapshot_consumer.py`, `test_postgresql_repository.py`.
|
||||
- 핸들러/e2e(resolver 람다 + payload portfolio_id): `test_messaging_intent_consumer.py`(+ 신규 3: without_portfolio_id_rejected / unknown_portfolio_rejected / symbol_resolved_from_universe), `test_rabbitmq_intent_e2e.py`.
|
||||
- ledger skip(admission 우회 + caplog): `test_execution_ledger.py`(skip 테스트 재작성 + 신규 `test_ledger_skip_emits_warning`).
|
||||
- 신규 PG 게이트: `tests/adapters/persistence/test_ops_checks.py`(gap 탐지 / 정상 0건).
|
||||
|
||||
## 검증 실행
|
||||
- 단위: `TREX_BROKER=paper .venv/bin/python -m pytest -q` → `282 passed / 32 skipped`(`::test_sandbox_token_and_submit` 는 LIVE `.env`(sandbox URL 아님)로 deselect — 본 batch 무관).
|
||||
- 인프라 게이트: `TREX_POSTGRESQL_TEST_DSN=...(trex_core_test) TREX_RABBITMQ_TEST_URL=... pytest -q` → `314 passed`(PG anti-join 점검 + buy intent RabbitMQ e2e + 마이그레이션 round-trip 포함).
|
||||
- `ruff check trex_core tests` → All checks passed.
|
||||
@@ -0,0 +1,39 @@
|
||||
# BAT-015: buy intent portfolio_id 필수 계약 + symbol 서버 resolve + ledger skip 가시화 + 누락 점검 쿼리
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-011
|
||||
- Included REQ: REQ-007 (CHG-20260608-01), REQ-018 (CHG-20260608-01), REQ-021 (CHG-20260608-01), REQ-055
|
||||
|
||||
## Summary
|
||||
- dcy-011(buy intent — portfolio_id 필수 / symbol 서버 resolve + ledger silent skip 가시화)을 단일 standard batch 로 delivery 한다. issue #6(buy 식별자 정정)과 issue #11(portfolio_id 없는 체결의 ledger silent skip)을 한 주기로 흡수한다.
|
||||
- buy intent 계약을 hard cutover: `portfolio_id` 필수화 + admission 시 `SnapshotStore` universe 에서 `symbol` resolve + universe 부재 시 `unknown_portfolio` 거부(REQ-007), buy 메시지 payload 에서 `symbol` 제거(REQ-021), `_record_ledger` skip 가시화(WARN 로그 + `ExecutionResult.ledger_skipped` 카운터, REQ-018), closed↔`ledger_outbox` anti-join 운영 점검 `find_ledger_gap_slots` 신규(REQ-055).
|
||||
- 거버넌스: dcy-011 OQ-3 결정에 따라 기존 Implemented REQ(007/018/021)는 `change-req`, 기존 REQ 가 다루지 않는 신규 기능(FR-5 운영 점검 쿼리)만 신규 REQ-055.
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- `BuyIntent` portfolio_id 필수화 + symbol Optional(서버 resolve), `IntentService(resolve_symbol=...)` 주입, `_admit_buy` portfolio_id_required/unknown_portfolio reject + symbol resolve (REQ-007)
|
||||
- buy 메시지 핸들러 payload 에서 symbol 제거·portfolio_id 매핑 (REQ-021)
|
||||
- `runtime/daemon.py` `_resolve_symbol`(SnapshotStore.get) 배선 (REQ-007)
|
||||
- `_record_ledger` 두 skip 경로(portfolio_id/execution_id 누락) WARN 로그 + 카운터 가시화 (REQ-018)
|
||||
- `adapters/persistence/ops_checks.py::find_ledger_gap_slots` 읽기 전용 anti-join 점검 (REQ-055)
|
||||
- `docs/interface-contract.md` + `docs/runtime-flows.md` 정합 (FR-6, REQ-007/021/018 본문에 흡수)
|
||||
- Out of Scope:
|
||||
- sell intent 계약 변경(이미 portfolio_id/slot 기반)
|
||||
- universe refresh 주기/스케줄 재설계
|
||||
- symbol-only 레거시 호환 창구(OQ-2 hard cutover)
|
||||
- `ledger_outbox` skip 마커 행 도입(OQ-4 — 로그+메트릭만, 영구 탐지는 REQ-055 anti-join 위임)
|
||||
- 과거 누락분 데이터 복구(별도 운영 이슈)
|
||||
- skip 점검 쿼리의 알람/주기 실행 배선(쿼리/함수가 산출물; 운영 배포 절차)
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- Source discovery 단일(dcy-011). OQ-1~OQ-5 모두 Answered.
|
||||
- hard cutover(NFR-3): symbol 기반 호출부(테스트 다수) 동시 전환. `IntentService(resolve_symbol=None)` 는 `intent.symbol` 로 폴백 → 비-핸들러 테스트 churn 최소화, 프로덕션 daemon 은 항상 SnapshotStore resolve.
|
||||
- ledger skip 가시화(REQ-018)는 전이 동작 불변, 관측만 추가(OQ-4).
|
||||
- REQ-055 anti-join 은 `daily_slot` durable 특성에 의존(outbox 마커 없이 영구 탐지).
|
||||
@@ -0,0 +1,53 @@
|
||||
# BAT-015 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-015
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-007 (Interface, change-req CHG-20260608-01): buy admission 계약 — `portfolio_id` 필수화, admission 시 `SnapshotStore` 에서 symbol resolve, universe 부재 시 `unknown_portfolio` 거부. symbol-필수/portfolio_id-optional 폐기(hard cutover).
|
||||
- REQ-021 (Integration, change-req CHG-20260608-01): buy 메시지 payload 계약에서 `symbol` 제거, `portfolio_id` 필수.
|
||||
- REQ-018 (Integration, change-req CHG-20260608-01): `_record_ledger` ledger skip 가시화(WARN 로그 + `ledger_skipped` 카운터, 두 skip 경로).
|
||||
- REQ-055 (Exception, 신규): closed↔`ledger_outbox` anti-join 운영 점검 `find_ledger_gap_slots`(읽기 전용).
|
||||
|
||||
## Delivery Plan
|
||||
1. REQ-007: `BuyIntent`(portfolio_id 필수 + symbol Optional 서버 resolve), `IntentService(resolve_symbol=...)`, `_admit_buy`(portfolio_id_required → resolve → unknown_portfolio). intent 단위 + 동시성 회귀.
|
||||
2. REQ-021: buy 핸들러 payload 파싱(symbol 제거, portfolio_id 매핑). 핸들러 단위 + RabbitMQ intent e2e.
|
||||
3. REQ-007 배선: `runtime/daemon.py::_resolve_symbol`(SnapshotStore.get) + IntentService 주입.
|
||||
4. REQ-018: `_record_ledger` 두 skip 경로 WARN 로그 + 카운터. caplog 단위.
|
||||
5. REQ-055: `adapters/persistence/ops_checks.py::find_ledger_gap_slots` anti-join SQL. PG 게이트 단위.
|
||||
6. FR-6 문서: interface-contract / runtime-flows 정합.
|
||||
7. 전체 회귀 + ruff + 인프라 게이트(PG + RabbitMQ).
|
||||
|
||||
## Dependencies
|
||||
- Internal:
|
||||
- dcy-002/BAT-002 intent 제출(REQ-007) — admission 변경 대상.
|
||||
- dcy-006/BAT-007 messaging 핸들러(REQ-021) — buy payload 파싱 변경 대상.
|
||||
- dcy-004/BAT-006 fill→ledger(REQ-018) — `_record_ledger` skip 가시화 대상.
|
||||
- dcy-005/BAT-008 SnapshotStore(REQ-027/028) — portfolio_id→symbol resolve 소스.
|
||||
- REQ-019 ledger outbox 스키마 — REQ-055 anti-join 대상(스키마 변경 없음).
|
||||
- External:
|
||||
- PostgreSQL(anti-join 점검). RabbitMQ(buy intent e2e).
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: (a) buy 계약 hard cutover 는 `BuyIntent` 구조 + admission + 메시지 핸들러 + daemon 배선 + 다수 테스트 표면에 동시 영향 — symbol 서버 resolve 의 주입 경계(`resolve_symbol` 콜백 vs SnapshotStore 직결)와 폴백 규칙을 설계에서 고정해야 회귀를 막는다. (b) reject 정책 분기(portfolio_id_required vs unknown_portfolio)와 universe 부재 판정 경로를 명확히 해야 admission 의미가 일관된다. (c) ledger skip 가시화(REQ-018)와 anti-join 점검(REQ-055)의 책임 분리(런타임 관측 vs 사후 탐지) + OQ-4 결정(outbox 마커 미도입)을 설계에서 못박아야 스키마 오염을 피한다. → design.md 동일 턴 scaffold.
|
||||
|
||||
## Milestones
|
||||
1. M1 — REQ-007 BuyIntent + IntentService resolve_symbol + _admit_buy reject/resolve, intent 단위 green
|
||||
2. M2 — REQ-021 buy 핸들러 payload portfolio_id 기반, 핸들러 단위 + e2e green
|
||||
3. M3 — REQ-007 daemon _resolve_symbol 배선
|
||||
4. M4 — REQ-018 _record_ledger 가시화, caplog 단위 green
|
||||
5. M5 — REQ-055 find_ledger_gap_slots, PG 게이트 단위 green
|
||||
6. M6 — FR-6 문서 정합 + 전체 회귀 + ruff + 인프라 게이트
|
||||
|
||||
## Risks
|
||||
- R-1 (High): hard cutover 가 symbol 기반 기존 테스트/호출부 다수를 깬다. 대응: `IntentService(resolve_symbol=None)` 는 `intent.symbol` 폴백 → 비-핸들러 테스트는 portfolio_id 추가만으로 무회귀; 핸들러/e2e 는 resolver + payload portfolio_id 동시 전환. 모든 호출부 식별·전환(NFR-3).
|
||||
- R-2 (Medium): 매수 시점 universe 부재 시 정상 매수가 unknown_portfolio 로 거부. 대응: 단일 reject 정책(OQ-1) + 진단 reason/로그. 거래일 refresh 정합은 운영 점검(R-4와 연계).
|
||||
- R-3 (Low, OQ-4): skip 마커 행 미도입 → 영구 탐지는 REQ-055 anti-join 으로 충족. outbox 스키마/dispatcher 무변경.
|
||||
- R-4 (Medium, issue #11): 잔존/과거 누락 체결 탐지 지연. 대응: skip 가시화(REQ-018) + anti-join 점검(REQ-055). 과거분 복구는 범위 밖.
|
||||
|
||||
## Notes
|
||||
- hard cutover 1차 게이트: 전체 회귀가 green 이어야 호출부 전환 누락이 없음을 보장.
|
||||
- REQ-055 anti-join 은 PG 게이트(실 PostgreSQL) 통합으로 검증; gap 탐지 + 정상 0건.
|
||||
- buy intent e2e(RabbitMQ)는 payload portfolio_id + resolver 경로 확인.
|
||||
@@ -0,0 +1,65 @@
|
||||
# BAT-015 Verification
|
||||
|
||||
- Status: verified
|
||||
- Batch: BAT-015
|
||||
|
||||
## Acceptance Mapping
|
||||
|
||||
### REQ-007 (buy admission portfolio_id 필수 + symbol resolve, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| AC-1 idle→pending_buy admit (portfolio_id 기반) | tests/application/test_messaging_intent_consumer.py::test_buy_symbol_resolved_from_universe + test_intent_concurrency.py::test_concurrent_buys_admit_exactly_idle_count |
|
||||
| AC-2 sell 전이 (회귀 불변) | tests/application/test_execution_ledger.py (sell fill) + 전체 회귀 |
|
||||
| AC-3 멱등 재전송 | test_intent_concurrency.py::test_duplicate_intent_id_admits_once_under_concurrency |
|
||||
| AC-4 동시성 단일 writer | test_intent_concurrency.py::test_concurrent_buys_admit_exactly_idle_count |
|
||||
| AC-5 결정 재조회 | 전체 회귀(IntentService.lookup) |
|
||||
| portfolio_id_required 거부 (CHG delta) | test_messaging_intent_consumer.py::test_buy_without_portfolio_id_rejected |
|
||||
| unknown_portfolio 거부 (CHG delta) | test_messaging_intent_consumer.py::test_buy_unknown_portfolio_rejected |
|
||||
| symbol 서버 resolve (CHG delta) | test_messaging_intent_consumer.py::test_buy_symbol_resolved_from_universe |
|
||||
|
||||
### REQ-021 (buy payload symbol 제거, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| buy payload portfolio_id 매핑(symbol 미사용) | test_messaging_intent_consumer.py (3 buy 테스트) |
|
||||
| RabbitMQ buy intent e2e | tests/adapters/messaging/test_rabbitmq_intent_e2e.py (인프라 게이트) |
|
||||
|
||||
### REQ-018 (ledger skip 가시화, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| AC-1 buy fill → 원장 1건 + holding | tests/application/test_execution_ledger.py::test_buy_fill_enqueues_one_ledger_item_and_writer_records_it |
|
||||
| AC-2 sell fill → 원장 1건 + closed | test_execution_ledger.py::test_sell_fill_enqueues_sell_ledger_item |
|
||||
| AC-3 FillStatus execution_id/fee | 위 buy 테스트 |
|
||||
| AC-4 skip 시 전이 정상 + **가시화(WARN+카운터)** | test_execution_ledger.py::test_portfolio_id_absent_skips_ledger_but_transitions_slot + ::test_ledger_skip_emits_warning (caplog) |
|
||||
| AC-5 단일 기록 주체 | test_domain_import_sweep + execution.py 단일 enqueue 지점 |
|
||||
|
||||
### REQ-055 (closed↔outbox anti-join 운영 점검, 신규)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| AC-1 portfolio_id NULL closed 포함 (S-4) | tests/adapters/persistence/test_ops_checks.py::test_detects_only_gap_slots (PG 게이트) |
|
||||
| AC-2 portfolio_id 있으나 outbox 부재 포함 | ::test_detects_only_gap_slots |
|
||||
| AC-3 정상(outbox 일치) 제외 → 0건 | ::test_no_gaps_when_all_recorded |
|
||||
| AC-4 비-closed 제외 | ::test_detects_only_gap_slots |
|
||||
|
||||
## Evidence
|
||||
- Test:
|
||||
- 단위 전체 회귀: `282 passed / 32 skipped`(`::test_sandbox_token_and_submit` deselect — LIVE `.env` sandbox URL 아님, 본 batch 무관).
|
||||
- 인프라 게이트(PG trex_core_test + RabbitMQ): `314 passed`(PG anti-join 점검 gap 탐지/정상 0건 + buy intent RabbitMQ e2e + 마이그레이션 round-trip 포함).
|
||||
- `ruff check trex_core tests` → All checks passed.
|
||||
- Logs: `282 passed, 32 skipped, 1 deselected` / `314 passed, 1 deselected ~15.5s`.
|
||||
|
||||
## 성공 기준(dcy-011) 대응
|
||||
- S-1(portfolio_id 누락 거부 + portfolio_id 만으로 resolve admit): test_buy_without_portfolio_id_rejected + test_buy_symbol_resolved_from_universe.
|
||||
- S-2(매수 슬롯 portfolio_id 귀속 + outbox/ledger 정합): buy fill 원장 테스트 + e2e.
|
||||
- S-3(ledger skip 무성 금지, WARN 100%): test_ledger_skip_emits_warning(caplog).
|
||||
- S-4(점검 쿼리 누락 정확 식별, 정상 0건): test_detects_only_gap_slots + test_no_gaps_when_all_recorded.
|
||||
- S-5(코드-문서 정합): interface-contract / runtime-flows 갱신(FR-6).
|
||||
|
||||
## Carried-Over / 비차단
|
||||
- 매수 시점 universe(SnapshotStore) portfolio_id 존재 보장은 운영 런타임 데이터(거래일 refresh 정합) — 부재는 `unknown_portfolio` reject 로 안전 처리(R-2).
|
||||
- REQ-055 점검 쿼리의 알람/주기 실행 배선은 운영 배포 절차(Out of Scope) — 쿼리/함수가 산출물.
|
||||
- 과거 누락분 데이터 복구는 본 주기 범위 밖(필요 시 별도 운영 이슈, R-4).
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues:
|
||||
- 없음. hard cutover 호출부 전환 누락은 전체 회귀(314 passed) green 으로 부재 확인. LIVE sandbox 토큰 테스트 deselect 는 `.env` 환경 의존(본 batch 무관).
|
||||
@@ -22,3 +22,4 @@
|
||||
| BAT-012 | standard | release-candidate | REQ-043, REQ-044, REQ-045, REQ-046, REQ-047 | dcy-008 | [bat-012](./bat-012_20260607_kill-switch-control/) |
|
||||
| BAT-013 | standard | release-candidate | REQ-048, REQ-049, REQ-050, REQ-051 | dcy-009 | [bat-013](./bat-013_20260607_multi-instance-topology/) |
|
||||
| BAT-014 | batch-lite | release-candidate | REQ-052, REQ-053, REQ-054 | dcy-010 | [bat-014](./bat-014_20260608_safety-sell-cooldown/) |
|
||||
| BAT-015 | standard | release-candidate | REQ-007 (CHG-20260608-01), REQ-018 (CHG-20260608-01), REQ-021 (CHG-20260608-01), REQ-055 | dcy-011 | [bat-015](./bat-015_20260608_buy-intent-portfolio-id-contract/) |
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
- 문서 ID: dcy-011_20260608_buy-intent-portfolio-id-required-symbol-resolve
|
||||
- 이슈명: buy intent — portfolio_id를 필수 키로, symbol은 universe(SnapshotStore)에서 resolve (+ portfolio_id 누락 체결의 ledger silent skip 가시화)
|
||||
- 작성 시각(KST): 2026-06-08 11:08 KST
|
||||
- 마지막 갱신 시각(KST): 2026-06-08 12:23 KST
|
||||
- 마지막 갱신 시각(KST): 2026-06-08 14:10 KST
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: 없음
|
||||
- 생성된 REQ 참조: REQ-007(change-req CHG-20260608-01), REQ-018(change-req CHG-20260608-01), REQ-021(change-req CHG-20260608-01), REQ-055(신규) — delivery: BAT-015
|
||||
|
||||
# 1. 계획 상태 요약
|
||||
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
- Baseline Seed: .stage-pilot/bootstrap/baseline.yaml
|
||||
- Status: draft
|
||||
- Owner: project maintainers
|
||||
- Last Updated (KST): 2026-06-07 (BAT-013)
|
||||
- Last Updated (KST): 2026-06-08 (BAT-015)
|
||||
- Source Discovery / Batch: bootstrap-baseline (pre-Discovery); HTTP API = dcy-002 / BAT-002
|
||||
- Primary Runtime: daemon (HTTP API) + library (slot 도메인)
|
||||
|
||||
@@ -81,11 +81,13 @@
|
||||
{ "intent_type": "buy",
|
||||
"intent_id": "<correlation_id 와 동일>",
|
||||
"trading_date": "YYYY-MM-DD",
|
||||
"symbol": str, "qty": float?, "allocated_capital": float?,
|
||||
"portfolio_id": str, # REQUIRED (dcy-011/REQ-007) — canonical key(asset_id)
|
||||
"qty": float?, "allocated_capital": float?,
|
||||
"entry_price": float?, "strategy_id": str?, "sell_strategy_id": str?,
|
||||
"portfolio_id": str?, "forced_close_at": "ISO-8601"?,
|
||||
"forced_close_at": "ISO-8601"?,
|
||||
"stop_loss_price": number?, "take_profit_price": number? }
|
||||
```
|
||||
- **`portfolio_id` 필수(dcy-011/REQ-007/021)**: 매수의 정식 식별자. 누락 시 admission 에서 `portfolio_id_required` 로 거부. **`symbol` 은 client payload 에 없다** — core 가 admission 시 universe(`SnapshotStore`)에서 `portfolio_id → symbol` 을 resolve 해 슬롯/KIS 주문에 사용한다. universe 에 portfolio_id 가 없으면 `unknown_portfolio` 로 거부(하위호환·symbol fallback 없음, hard cutover).
|
||||
- `stop_loss_price`/`take_profit_price`(선택, REQ-035/038): 매수 시 설정하면 워치독 만료 후 safety-sell 가격 트리거의 기준이 된다. 미포함 시 NULL(트리거 비활성).
|
||||
sell payload 는 `{"intent_type":"sell", "intent_id":..., "slot_id":..., "reason":..., "sell_strategy_id":...}`.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
- Baseline Seed: .stage-pilot/bootstrap/baseline.yaml
|
||||
- Status: draft
|
||||
- Owner: project maintainers
|
||||
- Last Updated (KST): 2026-06-08 (BAT-014)
|
||||
- Last Updated (KST): 2026-06-08 (BAT-015)
|
||||
- Source Discovery / Batch: bootstrap-baseline (pre-Discovery); 데몬/HTTP 흐름 = dcy-002 / BAT-002
|
||||
- Primary Runtime: daemon (HTTP API) + library (slot 도메인)
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
1. 전략 모듈이 RPC 요청을 `trex.intent.req` 큐로 publish — payload `{intent_type, intent_id, ...}`, properties `correlation_id` = intent 멱등키, `reply_to`.
|
||||
2. `trex-intent-rpc` 스레드(단일, prefetch=1)가 메시지 수신 → `IntentService.submit_buy/sell` 호출.
|
||||
3. `intent_log.try_insert(intent_id, ...)` 가 dedup 게이트 — 중복 메시지는 기존 decision 재반환(슬롯 무전이).
|
||||
4. 신규면 admission: `find_idle_slot` → `SlotService.transit_slot(idle→pending_buy)` → Postgres durable commit.
|
||||
4. 신규면 admission: **portfolio_id 필수 검사**(누락 시 `portfolio_id_required` 거부) → **symbol resolve**(`SnapshotStore.get(portfolio_id).symbol`, universe 부재 시 `unknown_portfolio` 거부, dcy-011/REQ-007) → `find_idle_slot` → `SlotService.transit_slot(idle→pending_buy, symbol=resolved, portfolio_id)` → Postgres durable commit. (buy payload 에 client symbol 없음)
|
||||
5. admission 응답(`status, slot_id?, reason?`)을 `reply_to` 큐로 publish → manual ack.
|
||||
6. ack-after-commit 시퀀스: durable commit 이 ack 보다 먼저이므로 ack 전 크래시 시 재전달돼도 (3) 의 dedup 으로 재실행되지 않는다.
|
||||
7. 체결/종료는 응답이 아니라 후속 이벤트(아래)로 전달 — admission 동기 + fill 비동기 2단계 시맨틱.
|
||||
@@ -197,6 +197,8 @@
|
||||
|
||||
집행 루프 fill 전이 직후 `ExecutionService` 가 ledger 기록을 outbox 에 적재해 슬롯 전이 critical path 와 ledger write 를 분리한다(원장 기록 단일 주체 = 집행 경로, NFR-3).
|
||||
|
||||
- ledger skip 가시화(dcy-011/REQ-018, issue #11): `_record_ledger` 가 `portfolio_id` 또는 `execution_id` 누락으로 skip 하면 무성 반환하지 않고 **WARN 로그 + `ExecutionResult.ledger_skipped` 카운터**로 노출한다(슬롯 전이는 그대로 진행). portfolio_id 필수화(REQ-007)로 주원인은 제거됐고, 잔여 누락은 운영 점검 쿼리(REQ-055: closed 슬롯 ↔ `ledger_outbox` anti-join, `find_ledger_gap_slots`)로 영구 탐지한다.
|
||||
|
||||
1. fill 감지: `BrokerPort.poll_fill` 이 `state="filled"` 와 결정적 `execution_id`(paper: `paper:{broker_order_id}:{filled_qty}` / KIS: `kis-daily-ccld:{ord_dt}:{ord_gno_brno}:{odno}:{tot_ccld_qty}`), 계산된 `fee`(BrokerPort `calculate_fee(side, amount)`), `tax=0` 을 운반.
|
||||
2. 슬롯 전이: `pending_buy→holding`(avg_price/qty) 또는 `pending_sell→closed` 를 advisory lock 안에서 먼저 commit. 이 단계가 critical path.
|
||||
3. ledger enqueue: 슬롯의 `portfolio_id`(== ledger `asset_id`, OQ-1 결정) 가 있으면 `LedgerTrade(asset_id, order_id, execution_id, side, qty, price, fee, tax, executed_at)` 을 `LedgerOutbox.enqueue` 로 적재. 행 식별자는 `correlation_id = {asset_id}:{order_id}:{execution_id}`(로컬 dedup). `portfolio_id` 부재면 ledger 기록 skip 후 슬롯 전이는 정상 유지.
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# req-055: closed 슬롯 ↔ ledger_outbox 누락 운영 점검 쿼리
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Exception
|
||||
- Priority: Medium
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
|
||||
portfolio_id 없는 체결의 ledger silent skip(issue #11)으로 발생하는 회계 누락을 운영자가 탐지·집계할 수 있도록, `closed` 슬롯 중 대응 `ledger_outbox` 행이 없는 케이스를 anti-join 으로 식별하는 읽기 전용 운영 점검을 제공한다(dcy-011 FR-5). `daily_slot` 이 durable 하므로 outbox 마커 행 없이도 영구 탐지가 성립한다(dcy-010 OQ-4 위임).
|
||||
|
||||
## Requirement
|
||||
|
||||
- `find_ledger_gap_slots(dsn, *, trading_date=None)` 읽기 전용 함수를 제공한다(`trex_core/adapters/persistence/ops_checks.py`).
|
||||
- gap 정의: `status='closed'` 슬롯 중 `portfolio_id IS NULL` 이거나 `ledger_outbox` 에 `asset_id = slot.portfolio_id` 행이 없는 슬롯.
|
||||
- 결과는 `slot_id`/`trading_date`/`symbol`/`portfolio_id`/`broker_order_id` 를 포함한다. `trading_date` 인자로 일자 범위를 한정할 수 있다(미지정 시 전체).
|
||||
- 스키마 변경 없음(읽기 전용 SELECT). 주기/온디맨드 실행으로 누락을 탐지·집계할 수 있어야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given `closed` 슬롯 중 `portfolio_id IS NULL` 인 슬롯, When `find_ledger_gap_slots`, Then 그 슬롯이 결과에 포함된다(S-4).
|
||||
2. Given `closed` 슬롯 중 portfolio_id 는 있으나 대응 outbox 행이 없는 슬롯, When 호출, Then 결과에 포함된다.
|
||||
3. Given `closed` 슬롯 + 대응 `ledger_outbox` 행(asset_id 일치)이 있는 슬롯, When 호출, Then 결과에 포함되지 않는다(정상 케이스 0건).
|
||||
4. Given `holding` 등 비-closed 슬롯, When 호출, Then 결과에서 제외된다(closed 한정).
|
||||
|
||||
## Impacted Area
|
||||
|
||||
- Docs: `docs/runtime-flows.md`(ledger skip 가시화 + 점검 쿼리 참조)
|
||||
- Modules: `trex_core/adapters/persistence/ops_checks.py`(신규, 읽기 전용)
|
||||
- Tests: `tests/adapters/persistence/test_ops_checks.py`(PG 게이트)
|
||||
|
||||
## Notes
|
||||
|
||||
- OQ-4 결정: outbox 마커 행 미도입 → slots↔outbox anti-join 으로 영구 탐지(REQ-019 outbox 스키마 변경 없음).
|
||||
- 알람/주기 실행 연결은 운영 배포 절차로 다룬다(쿼리/함수가 산출물).
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260608-01
|
||||
|
||||
- Date: 2026-06-08
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- dcy-011(confirmed) → REQ-055 생성(draft-req, Proposed). issue #11 제안 ③: closed↔outbox 대조 운영 점검(FR-5). 기존 REQ 가 다루지 않는 신규 기능이라 신규 REQ(OQ-3 결정).
|
||||
|
||||
#### Intent
|
||||
|
||||
- ledger 누락 영구 탐지 수단을 운영자에게 제공.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Added: AC-1~4 전체
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: BAT-015
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: no
|
||||
- Status Recommendation: keep-current
|
||||
|
||||
### CHG-20260608-02
|
||||
|
||||
- Date: 2026-06-08
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- Proposed → Approved (confirm-req). 필수 필드·구체 AC·dcy-011 FR-5 정합 확인.
|
||||
|
||||
### CHG-20260608-03
|
||||
|
||||
- Date: 2026-06-08
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- Approved → Implemented (confirm-req-implemented, 근거 BAT-015 release-candidate). `find_ledger_gap_slots` 구현 + PG 게이트 단위(gap 탐지/정상 0건) green. 전체 인프라 게이트 314 passed.
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: BAT-015 (release-candidate)
|
||||
- Releases: none
|
||||
@@ -149,3 +149,43 @@
|
||||
- AC-3: 위 buy 테스트가 FillStatus.execution_id/fee/tax 운반 직접 검증.
|
||||
- AC-4: tests/application/test_execution_ledger.py::test_portfolio_id_absent_skips_ledger_but_transitions_slot.
|
||||
- AC-5: tests/domain/test_domain_import_sweep.py + grep 결과(trex_core/application/execution.py 단일 enqueue 지점).
|
||||
|
||||
### CHG-20260608-01
|
||||
|
||||
- Date: 2026-06-08
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- (change-req, dcy-011/issue #11) `_record_ledger` 의 ledger skip 을 무성에서 **가시화**로 전환: portfolio_id/execution_id 누락 skip 시 WARN 로그 + `ExecutionResult.ledger_skipped` 카운터 노출(슬롯 전이는 그대로). BAT-015 재구현·재검증 완료 → Implemented 유지.
|
||||
|
||||
#### Intent
|
||||
|
||||
- portfolio_id 없는 체결의 ledger silent skip(회계 누락)을 운영자가 탐지 가능하게(issue #11 제안 ②). OQ-4 결정으로 outbox 마커 행은 도입하지 않고 로그+메트릭만; 영구 탐지는 REQ-055 점검 쿼리에 위임.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Updated:
|
||||
- AC-4: skip 시 무성 반환 → WARN 로그 + 카운터 가시화(두 skip 경로 모두).
|
||||
|
||||
#### Impacted Area Delta
|
||||
|
||||
- Modules: `trex_core/application/execution.py`(`_record_ledger` 로그 + `_log` 추가)
|
||||
- Docs: `docs/runtime-flows.md`
|
||||
- Tests: tests/application/test_execution_ledger.py::test_ledger_skip_emits_warning
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: BAT-015
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: yes (BAT-015 에서 완료)
|
||||
- Existing Implementation Invalidated: no (관측만 추가, 전이 동작 불변)
|
||||
- Status Recommendation: Implemented (change-req → BAT-015 재검증)
|
||||
- Follow-up Action: confirm-req-implemented(BAT-015).
|
||||
|
||||
#### Validation Plan
|
||||
|
||||
- BAT-015: ledger skip WARN 로그 단위(caplog) + 회귀.
|
||||
|
||||
@@ -142,3 +142,43 @@
|
||||
- AC-3: tests/application/test_messaging_intent_consumer.py + test_messaging_event_publisher.py 조합.
|
||||
- AC-4: tests/application/test_messaging_event_publisher.py::test_event_for_strategy_a_does_not_reach_strategy_b_queue + tests/adapters/messaging/test_rabbitmq_transport.py::test_event_publish_and_per_strategy_consume.
|
||||
- AC-5: tests/adapters/messaging/test_topology.py.
|
||||
|
||||
### CHG-20260608-01
|
||||
|
||||
- Date: 2026-06-08
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- (change-req, dcy-011) buy intent **메시지 계약**에서 `symbol` 필드 제거, `portfolio_id` 필수화. buy 핸들러는 payload 에서 symbol 을 읽지 않고 portfolio_id 만 매핑(서버 resolve 는 REQ-007). BAT-015 재구현·재검증 완료 → Implemented 유지.
|
||||
|
||||
#### Intent
|
||||
|
||||
- 요청 매핑 원칙(payload 식별자)을 정식 식별자(portfolio_id) 기준으로 정정(issue #6). buy 핸들러 payload 파싱 변경.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Updated:
|
||||
- buy payload 계약: `symbol` 제거, `portfolio_id` 필수.
|
||||
|
||||
#### Impacted Area Delta
|
||||
|
||||
- Modules: `trex_core/application/messaging_handlers.py`(buy 핸들러)
|
||||
- Docs: `docs/interface-contract.md`
|
||||
- Tests: tests/application/test_messaging_intent_consumer.py + tests/adapters/messaging/test_rabbitmq_intent_e2e.py
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: BAT-015
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: yes (BAT-015 에서 완료)
|
||||
- Existing Implementation Invalidated: yes (symbol 기반 buy payload)
|
||||
- Status Recommendation: Implemented (change-req → BAT-015 재검증)
|
||||
- Follow-up Action: confirm-req-implemented(BAT-015).
|
||||
|
||||
#### Validation Plan
|
||||
|
||||
- BAT-015: buy payload portfolio_id 기반 핸들러 단위 + RabbitMQ e2e.
|
||||
|
||||
@@ -153,3 +153,45 @@
|
||||
|
||||
#### Validation Plan
|
||||
- BAT-002 verification evidence 재참조로 충분.
|
||||
|
||||
### CHG-20260608-01
|
||||
|
||||
- Date: 2026-06-08
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- (change-req, dcy-011) buy intent admission 계약 변경: `portfolio_id` 필수화 + admission 시 universe(`SnapshotStore`) 에서 `symbol` resolve + universe 부재 시 `unknown_portfolio` 거부. 기존 symbol-필수/portfolio_id-optional 폐기(hard cutover, 하위호환 없음). BAT-015 에서 재구현·재검증 완료 → Implemented 유지.
|
||||
|
||||
#### Intent
|
||||
|
||||
- 정식 식별자(asset_id=portfolio_id) 기반 원장 귀속·safety 보호 범위 정합(issue #6). `_admit_buy` 에 portfolio_id_required/unknown_portfolio reject + symbol resolve(`resolve_symbol` 주입) 추가.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Added:
|
||||
- buy 는 portfolio_id 필수(누락 시 `portfolio_id_required`), symbol 은 서버 resolve(`SnapshotStore`), universe 부재 시 `unknown_portfolio` 거부.
|
||||
- Updated:
|
||||
- 기존 admission AC 의 symbol 입력 전제 → portfolio_id 기반으로 정정.
|
||||
|
||||
#### Impacted Area Delta
|
||||
|
||||
- Modules: `trex_core/application/intents.py`(BuyIntent portfolio_id 필수+symbol resolve, IntentService.resolve_symbol), `messaging_handlers.py`(buy payload), `runtime/daemon.py`(resolver 배선)
|
||||
- Docs: `docs/interface-contract.md`, `docs/runtime-flows.md`
|
||||
- Tests: intent/messaging buy 테스트 portfolio_id 기반 전환
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: BAT-015
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: yes (BAT-015 에서 완료)
|
||||
- Existing Implementation Invalidated: yes (symbol-필수 admission 경로)
|
||||
- Status Recommendation: Implemented (change-req → BAT-015 재구현·재검증)
|
||||
- Follow-up Action: confirm-req-implemented(BAT-015) 로 Implemented 동기화.
|
||||
|
||||
#### Validation Plan
|
||||
|
||||
- BAT-015 verification: portfolio_id_required/unknown_portfolio/symbol resolve 단위 + 전체 회귀(314 passed).
|
||||
|
||||
+3
-2
@@ -74,7 +74,7 @@
|
||||
|
||||
## Next Requirement ID
|
||||
|
||||
- Current: `REQ-055`
|
||||
- Current: `REQ-056`
|
||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||
|
||||
## Requirement Register
|
||||
@@ -135,11 +135,12 @@
|
||||
| REQ-052 | safety-sell per-slot 재시도 쿨다운 + 영속 거부 경고 | Integration | Implemented | High | project maintainers | [req-052](./Integration/req-052_safety-sell-per-slot-cooldown.md) |
|
||||
| REQ-053 | safety-sell 재시도 쿨다운 env 설정 2종 | Configuration | Implemented | Medium | project maintainers | [req-053](./Configuration/req-053_safety-sell-retry-cooldown-env.md) |
|
||||
| REQ-054 | safety-sell 쿨다운 runtime-flows 정합 | Documentation | Implemented | Low | project maintainers | [req-054](./Documentation/req-054_safety-sell-cooldown-baseline-alignment.md) |
|
||||
| REQ-055 | closed 슬롯 ↔ ledger_outbox 누락 운영 점검 쿼리 | Exception | Implemented | Medium | project maintainers | [req-055](./Exception/req-055_ledger-gap-ops-check.md) |
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-06-08 :: REQ-007/018/021 change-req + REQ-055 생성·Implemented (dcy-011, BAT-015): buy intent 계약 hard cutover — portfolio_id 필수화 + symbol 서버 resolve(SnapshotStore) + unknown_portfolio/portfolio_id_required 거부(REQ-007), buy payload symbol 제거(REQ-021), ledger skip 가시화(WARN+ledger_skipped 카운터, REQ-018), closed↔outbox anti-join 운영 점검 `find_ledger_gap_slots` 신규(REQ-055). 전체 인프라 게이트 314 passed, ruff clean. (change-req/draft-req/confirm-req-implemented)
|
||||
- 2026-06-08 :: REQ-052~054 Approved → Implemented (confirm-req-implemented): BAT-014(batch-lite) verify 완료(신규 8 단위, 전체 278 passed, ruff clean). SafetySellService per-slot 쿨다운(skip/만료재시도/경고/prune, market_close 제외), SafetySellConfig env 2종(min_retry_interval_sec 600/max_retry_warn_count 3), runtime-flows 정합. (confirm-req-implemented)
|
||||
- 2026-06-08 :: REQ-052~054 Proposed → Approved (confirm-req): dcy-010 safety-sell 쿨다운 3개 전원 승인 게이트 통과. dcy-010(confirmed) FR/OQ 와 모순 없음, 필수 필드·구체 AC 완비. 피드백 채널·DB 영속은 Out of Scope 비핵심. (confirm-req)
|
||||
- 2026-06-08 :: REQ-052~054 생성 (draft-req): dcy-010(safety-sell 영속 거부 재시도 백오프, confirmed) → 3개 REQ 초안. per-slot 쿨다운+경고(052), 쿨다운 env 2종(053), runtime-flows 정합(054). OQ-1(600s)/OQ-2(3회 경고)/OQ-3(단일 per-slot) Answered. (draft-req)
|
||||
- 2026-06-07 :: REQ-048~051 Approved → Implemented (confirm-req-implemented): BAT-013 verify 완료(신규 9 단위 + RabbitMQ 라우팅 2, 단위 271 passed, 인프라 게이트 301 passed(directed 격리 S-2 + broadcast dual S-3 + default 무회귀 S-4), ruff clean). per-instance TOML 로더+`--config`, instance_key 동적 큐명(default 리터럴 하위호환), trex.direct/trex.broadcast exchange+바인딩, baseline 3문서 정합. (confirm-req-implemented)
|
||||
- 2026-06-07 :: REQ-048~051 Proposed → Approved (confirm-req): dcy-009 multi-instance 4개 전원 승인 게이트 통과. dcy-009(confirmed) FR/OQ 와 모순 없음, 필수 필드·구체 AC 완비. strategy publish 변경·TOML 추가필드는 비핵심 후속. (confirm-req)
|
||||
|
||||
Reference in New Issue
Block a user