## 구현 완료 포트폴리오 소유자가 manual·broker를 포함한 모든 출처의 원장을 cursor 기반으로 조회하는 통합 API를 로컬 `main`에 구현·검증·커밋했습니다. ### 구현 내용 - `GET /v1/portfolios/{portfolio_id}/ledger`를 추가해 모든 source의 원장을 `recorded_at DESC, ledger_entry_id DESC` 순서로 반환합니다. - optional exact `source` filter, HMAC-signed opaque cursor 및 portfolio/source 조건 결속을 구현했습니다. malformed·tampered·다른 조건의 cursor는 HTTP 422로 거부합니다. - 통합 item은 canonical 대문자 `action`만 노출하며 `side`는 노출하지 않습니다. 출처별로 없는 사실은 추정값 대신 nullable field로 표현합니다. - PostgreSQL repository keyset query와 `(portfolio_id, recorded_at DESC, ledger_entry_id DESC)` index migration (`20260820_0030`)을 추가했습니다. - source filter, owner/service-token 권한 경계, cursor 오류, OpenAPI, release migration-head contract 및 `temp/` 제외 source-surface 검사를 회귀로 보완했습니다. ### 검증 결과 전체 회귀와 변경 범위 검증을 완료했습니다. - `pytest -q` → `664 passed, 16 skipped` - focused API·release·auth-surface 검증 → `29 passed` - disposable PostgreSQL fresh migration → head `20260820_0030`, `idx_portfolio_ledger_portfolio_recorded_entry_desc` 생성 확인 - 변경 범위 lint 및 `git diff --check` → 통과 - 로컬 커밋 → `ac742b1150832f6c1cf58744308b4f3ea6803fd4` (`feat(portfolios): add unified ledger history API`) ### 비차단 후속 권고 - 운영 환경에서는 `PORTFOLIOHUB_LEDGER_CURSOR_SECRET`를 장기 랜덤 secret으로 설정하고, cursor secret 교체 시 기존 cursor의 유효기간·무효화 정책을 운영 문서에 명시하는 것을 권고합니다. - 실제 운영 규모의 ledger row가 있는 환경에서 `EXPLAIN ANALYZE`로 source-filter 포함 query의 index 사용과 latency를 추가 관찰하는 것을 권고합니다. > 현재 상태는 **로컬 구현·검증·커밋 완료**입니다. push·PR·배포·릴리스 완료를 의미하지 않습니다.