Files
portfolio-hub/docs/srs/Interface/req-029_v1-portfolios-ledger-trades-migration.md
T
genichinandClaude Sonnet 4.6 4bf6b554a7 feat: v1 portfolio mutation, sub-resources, ledger path migration (BAT-008~011)
dcy-003/004 기반 v1 API surface 완성 및 BAT-008~011 구현·검증 완료:

- BAT-008 (REQ-019~021,024): 토큰 surface baseline 문서 갱신
- BAT-009 (REQ-025~028): POST/PATCH/DELETE/GET /v1/portfolios CRUD 신설
  - _require_write_token, _get_portfolio_or_404, soft delete, daemon-scoped list
- BAT-010 (REQ-030~032): portfolio tag/comment 서브 라우트 신설 + baseline 갱신
  - GET/POST/DELETE /v1/portfolios/{id}/tags
  - POST/GET/PATCH/DELETE /v1/portfolios/{id}/comments
- BAT-011 (REQ-029): ledger path migration
  - POST /v1/assets/{asset_id}/ledger/trades → /v1/portfolios/{portfolio_id}/ledger/trades
  - v1_ledger.py 제거, disabled 차단(409) 추가
  - test_v1.py / test_daemon_assignment.py 구 path 갱신
- 신규 tests: test_v1_portfolios_crud.py, test_v1_portfolios_list.py,
  test_v1_portfolios_tags_comments.py, test_v1_portfolios_ledger.py
- 전체 suite: 340 passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 07:25:39 +00:00

139 lines
7.6 KiB
Markdown

# REQ-029: POST /v1/portfolios/{portfolio_id}/ledger/trades 이전
- Status: Implemented
- Type: Interface
- Priority: High
- Owner: Justin Ko
## Intent
- 기존 `POST /v1/assets/{asset_id}/ledger/trades`(`app/api/v1_ledger.py`)의 path param 이름이 `asset_id`이나 실제 값은 `portfolios.portfolio_id`(UUID)로 의미 혼용되는 결함을 해소한다.
- dcy-003 옵션 B(path 명명 분리)에 따라 신 path `POST /v1/portfolios/{portfolio_id}/ledger/trades`로 이전하고 구 path는 동일 release에서 제거한다. 동작·인가·idempotency는 그대로 보존.
## Requirement
- 신규 라우트 `POST /v1/portfolios/{portfolio_id}/ledger/trades` (Bearer 인증 필수).
- **동작은 기존 `POST /v1/assets/{asset_id}/ledger/trades`와 동일하게 보존**:
- **daemon assignment 3단계 fail-closed 인가**: (1) token `daemon_id IS NULL` → HTTP 403, (2) portfolio `managed_by_daemon_id IS NULL`(미배정 asset) → HTTP 403, (3) `portfolio.managed_by_daemon_id != auth.daemon_id`(불일치) → HTTP 403.
- **idempotency**: `(portfolio_id, order_id, execution_id)` unique constraint (기존 `uq_ledger_trades_idempotency` 재사용).
- **duplicate/conflict 응답**: 동일 키 + 동일 payload → HTTP 200 + `status="duplicate"`. 동일 키 + 다른 payload → HTTP 409.
- **새 ledger row** → HTTP 201 + `status="new"`.
- **disabled portfolio 차단** (REQ-027 정합): `portfolios.status='disabled'`인 portfolio에 대해서는 HTTP 409 또는 422로 차단.
- 구 path `POST /v1/assets/{asset_id}/ledger/trades` 제거: 동일 batch release에서 라우트 삭제. 호출 시 HTTP 404.
- **trex-basic 동시 배포 필수** (dcy-003 OQ-3 + NFR-2): 신 path 단일 서빙. 한시적 별칭/redirect 없음. release plan에 배포 순서 잠금(trex-basic 클라이언트 사전 빌드 → portfolio-hub 배포 → trex-basic 배포) + rollback 시 trex-basic 동반 rollback 전제 명시.
## Acceptance Criteria
1. Given service token + 본인 daemon에 배정된 active portfolio + 신규 trade payload, When `POST /v1/portfolios/{portfolio_id}/ledger/trades`, Then HTTP 201 + `status="new"` + 생성된 ledger row.
2. Given AC-1 실행 후 동일 key + 동일 payload 재호출, When 호출, Then HTTP 200 + `status="duplicate"` + 단일 ledger row 유지.
3. Given AC-1 실행 후 동일 key + 다른 payload 호출, When 호출, Then HTTP 409 + ledger row 변경 없음.
4. Given service token `daemon_id IS NULL`, When `POST /v1/portfolios/{portfolio_id}/ledger/trades`, Then HTTP 403 fail-closed.
5. Given service token + `portfolio.managed_by_daemon_id IS NULL`(미배정 asset), When 호출, Then HTTP 403 fail-closed.
6. Given service token + `portfolio.managed_by_daemon_id != auth.daemon_id`(불일치), When 호출, Then HTTP 403 fail-closed.
7. Given service token + `status='disabled'` portfolio, When 호출, Then HTTP 409 또는 422 (disabled write 차단).
8. Given 비존재 `portfolio_id`, When 호출, Then HTTP 404.
9. Given 구 path `POST /v1/assets/{asset_id}/ledger/trades` 호출, When 본 REQ 구현 후 동일 batch release, Then HTTP 404.
10. Given Bearer token 없음, When 호출, Then HTTP 401.
## Impacted Area
- Docs:
- `docs/interface-contract.md` — POST /v1/portfolios/{id}/ledger/trades 명세 + 구 path 제거 (REQ-032)
- `docs/runtime-flows.md` — daemon assignment 3단계 인가 + idempotency + path 이전 흐름 (REQ-032)
- Modules:
- `app/api/v1_portfolios.py` — `POST /v1/portfolios/{portfolio_id}/ledger/trades` 라우트 추가 (또는 별도 `v1_ledger.py`를 신 path로 이전)
- `app/api/v1_ledger.py` — 구 path 제거 또는 신 path로 라우트 prefix 이전
- `app/main.py` — 라우터 include 갱신 (구 ledger 라우터 제거)
- `app/services/portfolio_service.py` 또는 `app/services/ledger_service.py` — 기존 ledger trade 로직 재사용 + disabled 차단 검증 추가
- Tests:
- 갱신 `tests/test_v1.py` — 구 path 케이스를 신 path로 갱신, 구 path 호출 시 HTTP 404 확인
- 신규 `tests/test_v1_portfolios_ledger.py` — AC-1~10 검증
## Notes
- 본 REQ는 기능 추가가 아닌 **path 이전**이라 기존 동작·테스트 시나리오를 그대로 재사용한다. 다만 disabled portfolio 차단(AC-7)은 REQ-027 soft delete 정책 정합 검증으로 신규.
- dcy-003 NFR-2(trex-basic 동시 배포)는 본 REQ의 가장 큰 운영 리스크(R-1)이며, release plan에 배포 순서 잠금·rollback 전제를 명시해야 함. release plan은 batch design/release 단계 작업.
- ledger_trades 테이블의 `asset_id` 컬럼명은 본 REQ에서 변경하지 않는다(dcy-001 `assets.asset_id`와 의미 혼용이 잔존하나, ledger_trades 컬럼은 portfolio_id를 가리킨다는 점이 path 이름 정합화로 충분히 명확해짐). 컬럼명 변경은 별도 migration REQ로 분리 가능.
- Source: dcy-003 FR-6, OQ-3, NFR-1, NFR-2, R-1 완화.
## Change Log
### CHG-20260521-01
- Date: 2026-05-21
- Author: Justin Ko
#### Change Summary
- Status: Approved → Implemented
#### Intent
- BAT-011 구현 완료: `app/api/v1_portfolios.py`에 `POST /{portfolio_id}/ledger/trades` 신설 (daemon 인가 3단계 + disabled 409 + idempotency 보존); `app/api/v1_ledger.py` 삭제; `app/main.py` include 정리; `tests/test_v1_portfolios_ledger.py` AC-1~10 신규; `tests/test_v1.py` + `tests/test_daemon_assignment.py` 구 path 갱신
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches: BAT-011 (release-candidate)
- Releases: pending
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: BAT-011 release candidate → draft-release; trex-basic 동시 배포 순서 잠금 필수
### CHG-20260520-02
- Date: 2026-05-20
- Author: Justin Ko
#### Change Summary
- Status: Proposed → Approved
#### Intent
- 승인 게이트 통과: 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 모두 충족, AC 구체성 확인 완료, blocker 없음
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches: BAT-011 (in-delivery)
- Releases: none (미정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: promote-to-approved
- Follow-up Action: BAT-011 draft-batch-planning 완료 → draft-batch-design 진행
### CHG-20260520-01
- Date: 2026-05-20
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-003 confirm-discovery 후 draft-req 실행. dcy-003 FR-6 산출물.
#### Intent
- ledger trade path 명명 정합화 + 동시 배포 정책 명시 + disabled 차단 추가.
#### Acceptance Criteria Delta
- Added:
- AC-1~10 최초 작성
#### Impacted Area Delta
- Docs: docs/interface-contract.md, docs/runtime-flows.md (REQ-032)
- Modules: app/api/v1_portfolios.py, app/api/v1_ledger.py(제거 또는 이전), app/main.py, app/services/* — 최초 지정
- Tests: tests/test_v1.py(갱신), tests/test_v1_portfolios_ledger.py(신규) — 최초 지정
#### Delivery Trace Delta
- Batches: none (미정)
- Releases: none (미정). trex-basic 동시 배포 release plan은 batch design 단계에서 작성.
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 계획 편입; trex-basic 측 release readiness(DATA_TREX_BASIC_RELEASE_WINDOW) 외부 확인 필요
#### Validation Plan
- pytest TestClient로 AC-1~10 검증; 신·구 path 회귀 + daemon 인가 3단계 + idempotency·conflict + disabled 차단