fix: align portfolio-hub ledger write adapter to live API contract
REQ-017 CHG-20260606-01. Live portfolio-hub returned 404 for the
trex-orbit-assumed POST /v1/assets/{asset_id}/ledger/trades. Correct to
POST /v1/portfolios/{portfolio_id}/ledger/trades with body keys
quantity/fee_amount/tax_amount (was qty/fee/tax), success 201. Reverified
with live e2e (201 Created, outbox COMPLETED) + durability drain of the
prior 404'd item. Resolves the REL-001 ledger-write release blocker.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
- adapters/broker: paper/KIS `FillStatus.execution_id` 합성, fee 계산 운반.
|
||||
- runtime: portfolio-hub config, ledger retry loop lifecycle.
|
||||
- persistence: ledger outbox work item store/schema.
|
||||
- Interfaces: `LedgerPort.record_trade(...)`, `FillStatus.execution_id/fee`, portfolio-hub `POST /v1/assets/{asset_id}/ledger/trades`.
|
||||
- Interfaces: `LedgerPort.record_trade(...)`, `FillStatus.execution_id/fee`, portfolio-hub `POST /v1/portfolios/{portfolio_id}/ledger/trades` (body `quantity`/`fee_amount`/`tax_amount`, 성공 201). (정정: REQ-017 CHG-20260606-01 — 구 `POST /v1/assets/{asset_id}/...`, body `qty`/`fee`/`tax` 폐기)
|
||||
|
||||
## Key Decisions
|
||||
1. `asset_id == portfolio_id`: 슬롯의 `portfolio_id` 를 ledger path param 으로 직접 사용한다. 매핑 조회나 asset master 동기화는 후속 범위다.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
## Acceptance Mapping
|
||||
- REQ-017:
|
||||
- AC-1: in-memory stub 원장 `record_trade(...)` 1회 호출 시 거래 1건 적재와 필드 보존 evidence.
|
||||
- AC-2: portfolio-hub adapter 가 `POST /v1/assets/{asset_id}/ledger/trades` 에 `execution_id` 포함 payload 를 전송하는 contract evidence.
|
||||
- AC-2: portfolio-hub adapter 가 `POST /v1/portfolios/{portfolio_id}/ledger/trades` 에 `execution_id` 포함 payload(body `quantity`/`fee_amount`/`tax_amount`)를 전송하는 contract evidence. (정정: REQ-017 CHG-20260606-01 — 아래 Re-verification Addendum)
|
||||
- AC-3: portfolio-hub 409 응답이 `LedgerConflictError` 또는 `conflict_blocked` 경로로 표면화되는 evidence.
|
||||
- AC-4: `trex_core/domain` portfolio-hub/HTTP 의존 0 import sweep evidence.
|
||||
- REQ-018:
|
||||
@@ -32,7 +32,7 @@
|
||||
- 명령: `.venv/bin/python -m pytest -q`
|
||||
- 결과: `109 passed, 12 skipped, 1 warning in 0.98s` (PG 4건 + KIS 1건 + 기존 PG 7건은 DSN/credentials 게이트로 skip).
|
||||
- REQ-017 AC-1: `tests/adapters/ledger/test_in_memory_ledger.py::test_record_trade_preserves_fields_and_is_idempotent_on_execution_id` — 단일 호출 후 `count()==1` + 필드 보존, 동일 execution_id 재호출 후에도 `count()==1`.
|
||||
- REQ-017 AC-2: `tests/adapters/ledger/test_portfolio_hub_ledger.py::test_post_contract_includes_execution_id_and_path_uses_asset_id` — `httpx.MockTransport` 로 path `/v1/assets/PF-77/ledger/trades`, body 에 `execution_id`/`side` 포함, `Authorization: Bearer …` 헤더 캡처.
|
||||
- REQ-017 AC-2: `tests/adapters/ledger/test_portfolio_hub_ledger.py::test_post_contract_includes_execution_id_and_path_uses_portfolio_id` — `httpx.MockTransport` 로 path `/v1/portfolios/PF-77/ledger/trades`, body 에 `execution_id`/`side`/`quantity`/`fee_amount`/`tax_amount` 포함(+`qty`/`fee`/`tax` 부재), `Authorization: Bearer …` 헤더 캡처. (정정: REQ-017 CHG-20260606-01 — 아래 Re-verification Addendum)
|
||||
- REQ-017 AC-3: `tests/adapters/ledger/test_portfolio_hub_ledger.py::test_409_surfaces_as_ledger_conflict_error` + `tests/application/test_execution_ledger.py::test_conflict_blocks_without_auto_retry` — 409 → `LedgerConflictError`; outbox row `status=conflict_blocked`, 후속 writer tick 에서 어댑터 재호출 없음.
|
||||
- REQ-017 AC-4: `tests/domain/test_domain_import_sweep.py::test_domain_has_no_forbidden_imports` — `trex_core/domain` AST 워크가 `httpx`/`fastapi`/`uvicorn`/`psycopg`/`redis`/`trex_core.adapters`/`trex_core.runtime`/`trex_core.application` 0 회 import 확인.
|
||||
- REQ-018 AC-1: `tests/application/test_execution_ledger.py::test_buy_fill_enqueues_one_ledger_item_and_writer_records_it` — buy intent → execution.run_once → slot=holding, ledger_enqueued=1, writer.run_once → ledger.count()=1, `LedgerTrade(asset_id="PF-1", side="buy", qty=10, price=1000.0)`.
|
||||
@@ -69,3 +69,11 @@
|
||||
- Notes:
|
||||
- portfolio-hub 실 계약/자격증명 evidence 는 stub + contract 수준이며, 실 통합 검증은 운영 단계에서 수집한다(dcy-004 DATA 위임 사항).
|
||||
- 부분체결 누적/정밀 세금 모델은 후속 Discovery 로 분리됨(이번 범위 밖).
|
||||
|
||||
## Re-verification Addendum (2026-06-06, REQ-017 CHG-20260606-01)
|
||||
- 트리거: RabbitMQ 매수 인텐트 → 체결 → ledger 기록 실 e2e 중 ledger write 가 live portfolio-hub 에서 404 발생. 원인은 어댑터가 구 orbit 가정 계약(`POST /v1/assets/{asset_id}/ledger/trades`, body `qty`/`fee`/`tax`)을 사용한 것. 실 계약은 openapi `LedgerTradeRequest` 기준 `POST /v1/portfolios/{portfolio_id}/ledger/trades` + `quantity`/`fee_amount`/`tax_amount`(성공 201).
|
||||
- 정정 구현: `trex_core/adapters/ledger/portfolio_hub.py` (path + body 키), `tests/adapters/ledger/test_portfolio_hub_ledger.py` (실 계약 단언).
|
||||
- 재검증 결과:
|
||||
- 단위/통합: `.venv/bin/python -m pytest tests/adapters/ledger/ tests/application/test_execution_ledger.py -q` → `16 passed`. `.venv/bin/ruff check trex_core/adapters/ledger tests/adapters/ledger` → All checks passed.
|
||||
- 실 e2e (live portfolio-hub 127.0.0.1:18000): 데몬 기동 → 매수 인텐트(005930, 10주, 300,000) 어드미션 → 슬롯 `idle→pending_buy→holding`(qty=10, avg_price=300000) → ledger writer `POST /v1/portfolios/{portfolio_id}/ledger/trades` **201 Created** → `ledger_outbox.status=completed`(last_outcome=completed). 직전 404 로 쌓였던 outbox 항목이 정정 후 재기동 시 무유실 드레인되어 REQ-019 durability 도 동시 실증(attempts 누적, 유실 0).
|
||||
- 영향: REQ-017 Implemented 유지(정정 후 실 계약 일치). REQ-018/019/020 evidence 는 영향 없음(포트/실행 경로/outbox 시맨틱 불변). REL-001(confirmed) 의 ledger write release blocker 해소.
|
||||
|
||||
Reference in New Issue
Block a user