# BAT-011 Implementation - Status: done - Batch: BAT-011 ## Plan Summary - `app/api/v1_portfolios.py`에 `POST /{portfolio_id}/ledger/trades` 추가 (daemon 인가 3단계 + disabled 차단 + idempotency). - `app/api/v1_ledger.py` 제거 + `app/main.py` include 정리. - `tests/test_v1_portfolios_ledger.py` 신규 (AC-1~10). - `tests/test_v1.py` 구 path 케이스 갱신 + HTTP 404 회귀 확인. ## Changed Files - `app/api/v1_portfolios.py` — `POST /{portfolio_id}/ledger/trades` 라우트 신설; `_require_service_token` 의존성(service token + daemon_id IS NULL 검증); `_payloads_match` 헬퍼; `LedgerTradeRequest/Response` import 추가 - `app/api/v1_ledger.py` — **삭제** (구 path 라우트 완전 제거) - `app/main.py` — `v1_ledger_router` import 및 `app.include_router(v1_ledger_router)` 제거 - `tests/test_v1_portfolios_ledger.py` — **신규** (AC-1~10: 10 tests) - `tests/test_v1.py` — 구 path 케이스 5건 신 path 갱신; `app.api.v1_ledger.*` 패치 → `app.api.v1_portfolios.*` 갱신; 파일 말미 중복 helper 2건 제거 - `tests/test_daemon_assignment.py` — `app.api.v1_ledger.*` 패치 → `app.api.v1_portfolios.*` 갱신; URL 4건 갱신; `_portfolio_with_daemon`에 `p.status = "enabled"` 추가 ## Execution Log 1. `app/api/v1_portfolios.py` — `Response` import 추가, `LedgerTradeRequest/LedgerTradeResponse` import 추가 2. `_require_service_token` 의존성 추가 (service token 검증 + daemon_id IS NULL 403) 3. `_payloads_match` 헬퍼 추가 (idempotency conflict 판정) 4. `POST /{portfolio_id}/ledger/trades` 라우트 추가 (disabled 409 → managed_by_daemon_id NULL 403 → daemon mismatch 403 → insert_ledger_trade) 5. `app/api/v1_ledger.py` 삭제 6. `app/main.py` 에서 v1_ledger 라우터 import + include 제거 7. `tests/test_v1.py` — 구 path URL 및 패치 경로 갱신 + 중복 helper 제거 8. `tests/test_daemon_assignment.py` — URL 및 패치 경로 갱신; `_portfolio_with_daemon` `status` 명시 9. `tests/test_v1_portfolios_ledger.py` — 신규 생성 (AC-1~10) ## Validation - `pytest tests/test_v1_portfolios_ledger.py -v`: 10 passed - `pytest --tb=short -q` (full suite): **340 passed, 0 failed** (2 warnings, non-blocking) ## Remaining Risks - R-1 (High): trex-basic 동시 배포 — release 단계 배포 순서 잠금 필수 (trex-basic 사전 빌드 완료 확인 → portfolio-hub 배포 → trex-basic 배포).