# BAT-023 Design - Status: confirmed - Batch: BAT-023 ## 설계 목표 - ledger write 응답의 portfolio-hub 권위 cash/quantity 를 스냅샷에 write-back 해 "수량 live·현금 stale" 비대칭을 제거(option D 활성화). 공백 구간은 추정 현금으로 메우되 안전·멱등·best-effort 를 보장. ## 1. 응답 계약 확장 (REQ-017) - `LedgerWriteOutcome{result: LedgerWriteResult, cash_balance_after: Optional[float], asset_quantity_after: Optional[float]}` 신설. `record_trade` 반환 타입 변경. - `portfolio_hub.py`: 2xx 응답 root flat `cash_balance_after`/`asset_quantity_after` 파싱(`_opt_num`, 누락/형변환 실패 시 None). new(201)/duplicate(200) 모두. 409→Conflict, 404/400/422→Permanent(dcy-015) 예외 유지. - `in_memory.py`: `cash_after`/`quantity_after` 주입 파라미터 → outcome 에 실어 반환(테스트용). ## 2. write-back (REQ-019/030, option D) - `LedgerWriter(snapshot_store=None)` 주입. `_process` 가 `new`·`duplicate` outcome 후 `_writeback(trade, outcome)` 호출. - `_writeback`: snapshot_store None 또는 `*_after` None 이면 skip. 아니면 `update_cash_position(trade.asset_id, cash_after, qty_after)`. **try/except 로 격리**(실패는 WARN, ledger outcome·청산 무차단, NFR-1). - duplicate 도 write-back(OQ-2 b): `*_after` 가 절대 권위값이라 멱등(중복 적용 위험 없음, NFR-3). 409/422 는 예외라 자연 skip. ## 3. 추정 현금 오버레이 (REQ-029 / OQ-4) - source = **미정산 outbox delta**(슬롯 아님): 슬롯 기반이면 write-back 후에도 슬롯이 holding 이라 double-count. 미정산(pending) outbox 행은 정확히 "fill∼write-back 공백" 집합. - `trade_cash_delta(trade)`: buy → -(qty*price + fee + tax), sell → +(qty*price - fee - tax)(코어 근사). - `LedgerOutbox.pending_cash_delta() -> {asset_id: Σdelta}`(InMemory: pending 행 합 / PG: SQL SUM CASE). 백오프 무관 전체 pending. - `_apply_cash_estimate(payload, ledger_outbox)`(snapshot 핸들러): pending delta 있는 자산에 `cash_balance += delta` + `cash_estimated:true`. best-effort(예외 시 응답 불변). - write-back 완료 → 행이 pending 이탈 → delta 0 → 추정 사라지고 store 는 권위값. 자연 수렴, double-count 없음. ## 4. 수량 source (REQ-029, OQ-1 B) - 슬롯 오버레이(`build_snapshot_envelope`) **변경 없음** — lifecycle 중 슬롯이 코어 권위 즉시값(불변식 A). write-back 은 현금 + 슬롯 밖 base 수량을 신선화. 슬롯↔write-back 불일치는 REQ-031 포지션 정합 경고로 노출(숨기지 않음). ## 5. daemon 배선 - 생성 순서: `SnapshotStore` 를 `LedgerWriter` 보다 먼저 생성(write-back target). - `LedgerWriter(snapshot_store=self.snapshot_store)`, snapshot 핸들러 `ledger_outbox=self.outbox`. ## 6. baseline 문서 (REQ-030 정합 포함) - interface-contract: `LedgerWriteOutcome`/`*_after`/write-back/envelope `cash_estimated`. - runtime-flows: ledger writer write-back 단계 + snapshot 응답 추정 오버레이. - REQ-030 Status Approved→Implemented(파일 + srs/index register). ## 테스트 설계 - adapter: `*_after` 파싱(new/duplicate/누락). - writer: write-back new/duplicate/skip(None)/best-effort(store 예외)/store 미주입; pending_cash_delta 합; estimate overlay + write-back 후 수렴. - PG 게이트: `pending_cash_delta` SQL. - 회귀: record_trade 반환 타입 변경에 따른 기존 ledger 테스트(outcome.result) 정합 — 기존 테스트 더블/소비처 전환.