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:
@@ -10,13 +10,13 @@
|
||||
|
||||
## Requirement
|
||||
- `LedgerPort.record_trade(asset_id, order_id, execution_id, side, qty, price, fee, tax, executed_at)` Protocol 을 application 레이어에 정의한다.
|
||||
- `PortfolioHubLedger` 어댑터는 trex-orbit `post_ledger_trade` 패턴을 따라 `POST /v1/assets/{asset_id}/ledger/trades` 로 거래를 기록하고, `execution_id` 로 멱등하며, portfolio-hub 가 반환하는 409 충돌을 `LedgerConflictError` 로 표면화한다.
|
||||
- `PortfolioHubLedger` 어댑터는 라이브 portfolio-hub 계약(`LedgerTradeRequest`)을 따라 `POST /v1/portfolios/{portfolio_id}/ledger/trades` 로 거래를 기록한다. 요청 body 는 `side`/`quantity`/`price`/`fee_amount`/`tax_amount`/`executed_at`/`execution_id`/`order_id` 키를 사용하며, `execution_id` 로 멱등하고, portfolio-hub 가 반환하는 409 충돌을 `LedgerConflictError` 로 표면화한다. (정정: CHG-20260606-01 — 구 orbit 가정 `POST /v1/assets/{asset_id}/...`, body `qty`/`fee`/`tax` 폐기)
|
||||
- 결정론 테스트용 stub/in-memory 원장 구현을 제공해 네트워크 없이 체결→기록 lifecycle 을 검증할 수 있게 한다.
|
||||
- 어댑터 자격증명/URL 은 .env(REQ-010 패턴, `TREX_PORTFOLIO_HUB_*`)로 주입한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given in-memory stub 원장, When `record_trade(...)` 를 1회 호출, Then 원장에 거래 1건이 적재되고 인자(asset_id/side/qty/price/fee/tax/executed_at)가 보존된다.
|
||||
2. Given portfolio-hub 어댑터, When `record_trade(...)`, Then `POST /v1/assets/{asset_id}/ledger/trades` 가 `execution_id` 를 포함해 호출된다(요청 계약은 orbit `post_ledger_trade` 와 동일).
|
||||
2. Given portfolio-hub 어댑터, When `record_trade(...)`, Then `POST /v1/portfolios/{portfolio_id}/ledger/trades` 가 `execution_id` 를 포함해 호출되고 요청 body 는 `side`/`quantity`/`price`/`fee_amount`/`tax_amount`/`executed_at`/`execution_id`/`order_id` 키를 사용한다(라이브 `LedgerTradeRequest` 계약, 성공 응답 201). (정정: CHG-20260606-01)
|
||||
3. Given portfolio-hub 가 409(중복/충돌)를 반환, When 어댑터가 응답을 처리, Then `LedgerConflictError` 로 표면화된다(예외를 삼키지 않는다).
|
||||
4. Given 정적 import 스윕, When `trex_core/domain` 을 점검, Then portfolio-hub/네트워크 의존이 0 이다(LedgerPort 는 application 계약, 어댑터는 adapters 레이어 격리). (NFR-1)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
## Notes
|
||||
- 출처: dcy-004 FR-1, NFR-1.
|
||||
- A-1 가정: portfolio-hub 는 orbit 과 동일 계약(`POST /v1/assets/{asset_id}/ledger/trades`, execution_id 멱등, 409 conflict)을 제공한다. 실제 계약/자격증명 검증은 dcy-004 confirm 시 REQ/delivery evidence 로 위임(비차단)됨 — 구현/검증 시 수집.
|
||||
- A-1 가정(폐기, CHG-20260606-01): orbit 동일 계약(`POST /v1/assets/{asset_id}/ledger/trades`, body `qty`/`fee`/`tax`) 가정은 라이브 portfolio-hub 실측과 불일치(404)로 확인돼 폐기. 실 계약은 `POST /v1/portfolios/{portfolio_id}/ledger/trades` + `LedgerTradeRequest`(`quantity`/`fee_amount`/`tax_amount`), 성공 201. execution_id 멱등은 유지 가정(중복 응답 코드는 후속 확인).
|
||||
- 도메인 순수성(NFR-1)을 별도 REQ 로 승격하지 않고 본 REQ 의 AC-4(import 스윕)로 흡수한다.
|
||||
|
||||
## Change Log
|
||||
@@ -147,3 +147,45 @@
|
||||
- AC-2: tests/adapters/ledger/test_portfolio_hub_ledger.py::test_post_contract_includes_execution_id_and_path_uses_asset_id.
|
||||
- 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.
|
||||
- AC-4: tests/domain/test_domain_import_sweep.py::test_domain_has_no_forbidden_imports.
|
||||
|
||||
### CHG-20260606-01
|
||||
- Date: 2026-06-06
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 실 portfolio-hub API 와의 ledger write 계약 불일치를 정정: 엔드포인트 `POST /v1/assets/{asset_id}/ledger/trades`(부재, 404) → **`POST /v1/portfolios/{portfolio_id}/ledger/trades`**, 요청 body 키 `qty`/`fee`/`tax` → **`quantity`/`fee_amount`/`tax_amount`**. `asset_id` 값은 슬롯 `portfolio_id`(REQ-018: asset_id == portfolio_id)라 `{portfolio_id}` path 세그먼트에 직접 매핑된다. 어댑터/테스트는 정정 구현·재검증 완료.
|
||||
|
||||
#### Intent
|
||||
- 가정(A-1, orbit `post_ledger_trade` 동일 계약)으로 작성됐던 ledger write 계약을 라이브 portfolio-hub 실측(openapi `LedgerTradeRequest`)에 맞춰 정정하고, 정정 구현의 재검증 evidence(신규 contract 테스트 + 실 e2e 201)를 연결한다.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added: none
|
||||
- Updated:
|
||||
- AC-2: portfolio-hub 어댑터 `record_trade(...)` 는 `POST /v1/portfolios/{portfolio_id}/ledger/trades` 를 호출하고, 요청 body 는 `side`/`quantity`/`price`/`fee_amount`/`tax_amount`/`executed_at`/`execution_id`/`order_id` 키를 사용한다(`execution_id` 포함, `execution_id` 멱등). 성공 응답은 201.
|
||||
- Removed: none
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- 본 REQ Requirement/AC-2/Notes 정정
|
||||
- BAT-006 design.md/verification.md 계약 정정 반영(같은 변경 묶음)
|
||||
- Modules:
|
||||
- trex_core/adapters/ledger/portfolio_hub.py (경로 + body 키 정정)
|
||||
- Tests:
|
||||
- tests/adapters/ledger/test_portfolio_hub_ledger.py (실 계약으로 갱신: path /v1/portfolios, body 키)
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-006 (release-candidate) — ledger write 계약 정정 반영, verification 재확인 대상
|
||||
- Releases:
|
||||
- REL-001 (confirmed) — BAT-006 포함. ledger write 경로가 live portfolio-hub 대상으로 동작하지 않던 release blocker 를 해소. release 문서는 직접 덮어쓰지 않고 본 항목으로 영향·후속 action 기록(rollout/verification 시 ledger POST 201 확인 항목 유효).
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: yes — 완료. 정정 어댑터에 대해 신규 contract 테스트 통과 + 실 portfolio-hub e2e(매수 체결 → `POST /v1/portfolios/{portfolio_id}/ledger/trades` 201 Created, outbox COMPLETED) 확인. 직전 404 로 쌓였던 outbox 항목이 정정 후 재기동 시 무유실 드레인되어 durability(REQ-019) 도 함께 실증.
|
||||
- Existing Implementation Invalidated: no — 정정 후 현재 코드가 실 계약과 일치. 무효화된 것은 구 가정(A-1)에 기반한 엔드포인트/필드 기술뿐.
|
||||
- Status Recommendation: Implemented 유지.
|
||||
- Follow-up Action: 멱등/중복 응답 시그널(현 어댑터는 2xx body `duplicate` 플래그 + 409 처리 유지) 은 실 API 가 중복 execution_id 에 어떤 코드를 반환하는지 확인되면 후속 정합.
|
||||
|
||||
#### Validation Plan
|
||||
- AC-2: tests/adapters/ledger/test_portfolio_hub_ledger.py::test_post_contract_includes_execution_id_and_path_uses_portfolio_id (path `/v1/portfolios/PF-77/ledger/trades` + body `quantity`/`fee_amount`/`tax_amount` 단언, `qty`/`fee`/`tax` 부재 단언).
|
||||
- AC-1/AC-3/AC-4: 기존 테스트 회귀(16 passed) 유지.
|
||||
- 실 e2e: 매수 intent → 체결 → ledger POST 201 + outbox completed.
|
||||
|
||||
Reference in New Issue
Block a user