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

7.6 KiB

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 차단