docs(dcy-009): 사용자용 read 엔드포인트 discovery confirmed + REQ-044/045 approved

- dcy-009: GET /v1/assets require_read 완화 + 사용자 가용 데몬 목록 신규 (trex-web 포트폴리오 관리 발원)
- REQ-044(assets 권한 완화) / REQ-045(사용자 데몬 목록) — Approved
- 전부 read 추가(쓰기·데몬관리 제외)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
2026-06-27 10:16:23 +09:00
co-authored by Claude Opus 4.8
parent 3a6adc98c9
commit d43ff430c2
5 changed files with 323 additions and 1 deletions
@@ -0,0 +1,151 @@
# 0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-009_20260627_user-read-asset-catalog-and-daemons
- 이슈명: 사용자용 read 엔드포인트 2종 — asset 카탈로그 조회 권한 완화(require_read) + 사용자 가용 데몬 목록 신규
- 작성 시각(KST): 2026-06-27 10:07
- 마지막 갱신 시각(KST): 2026-06-27 10:30
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 교차 서비스 발원(driver): **trex-web 포트폴리오 관리(예정 dcy-007)** — 사용자가 자신의 포트폴리오를 생성/변경할 때 ① asset 리스트에서 종목을 고르고 ② 자신이 사용 가능한 데몬을 선택해야 한다. 두 선택 UI에 필요한 read API가 portfolio-hub에 없다(asset 목록은 admin 전용, 사용자 데몬 목록은 라우트 미노출). 본 Discovery는 그 의존을 portfolio-hub측 변경 단위로 정식화한다. (trex-web 포트폴리오 관리 UI는 별도 trex-web dcy-007.)
- 생성된 REQ 참조: REQ-044, REQ-045
# 1. 계획 상태 요약
- 연계 문서 현황:
- 관련 점수 파일: 없음
- 관련 TODO/메모: 워크스페이스 GitHub 보드 #7 trex-web 항목. 발원 = trex-web 포트폴리오 관리.
- 해석:
- trex-web에서 "각 사용자가 자기 포트폴리오를 관리(생성/변경)"하려면 asset 선택·가용 데몬 선택이 필요한데, portfolio-hub의 해당 read 경로가 사용자에게 막혀 있다.
- 사장님 결정(2026-06-27): **GET /v1/assets를 require_read로 완화**, **사용자 가용 데몬 목록 read 엔드포인트 신규**. 백엔드를 먼저 만들고 trex-web UI를 얹는다.
# 2. 요구사항 판정 결과
- 판정: 신규
- 근거
- asset 카탈로그 조회 권한 완화는 기존 REQ(자산 카탈로그, admin 전용)의 접근 정책 변경 + 신규 사용자 데몬 목록 엔드포인트는 신규 인터페이스다.
- 포트폴리오 관리(생성/변경 시 선택 UI 공급)라는 새 소비 맥락에서 발생한 read 표면 확장이다.
# 3. 문제점의 요약
- 현재 상태
- `GET /v1/assets`(`app/api/v1_assets.py:75` `list_catalog_assets`)는 **`require_admin`** — 일반 사용자가 asset 카탈로그를 읽을 수 없다. (생성/비활성/태그 등 쓰기도 require_admin.)
- **사용자 가용 데몬을 조회하는 HTTP 엔드포인트가 없다.** 도메인 `Daemon`(id·owner_user_id·daemon_slug·display_name·is_active) 존재 + 서비스/리포지토리에 `list_daemons_for_user(user_id)`·`find_candidate_daemons_for_user(user_id)`가 있으나 라우트로 노출되지 않음.
- 포트폴리오 CRUD(`POST/PATCH/DELETE /v1/portfolios`)는 이미 owner-scoped `require_read`로 존재(이번 변경 대상 아님). 포트폴리오는 `managed_by_daemon_id`로 데몬에 바인딩됨(PATCH로 설정 가능).
- 기대 상태
- 일반 사용자(`require_read`)가 **active asset 카탈로그를 조회**할 수 있다(쓰기 권한은 admin 유지).
- 일반 사용자가 **자신이 사용 가능한 데몬 목록**(자기 소유 active 데몬)을 조회할 수 있다 — 포트폴리오의 `managed_by_daemon_id`로 선택할 후보.
- 이해관계자
- 사장님 — 접근 정책(카탈로그 공개 범위)·우선순위 결정
- 팀장(Claude) — Discovery/거버넌스 게이트
- portfolio-hub-dev — delivery(백엔드)
- trex-web(소비자) / 일반 사용자
- 제약사항
- asset 쓰기(생성/비활성/태그 적용)는 **admin 유지**(완화는 읽기에 한정).
- 데몬 목록은 **caller 본인(user_id) 스코프**(타 사용자 데몬 노출 금지).
- 기존 admin asset 관리·서비스 토큰 daemon 바인딩 동작에 회귀 없어야 함.
## 주요 사용자 시나리오
- 사용자가 trex-web에서 포트폴리오를 추가할 때, asset 선택 드롭다운/검색이 `GET /v1/assets`(read)로 active 카탈로그를 불러온다.
- 사용자가 포트폴리오의 관리 데몬을 고를 때, "내 가용 데몬" 목록을 신규 엔드포인트로 불러와 선택한다.
- admin은 기존대로 asset 카탈로그를 생성/수정/비활성(쓰기)한다(권한 불변).
# 4. 이번에 정의할 변경
- 변경 1 — **GET /v1/assets 권한 완화**: `require_admin` → `require_read`. status 필터(기본 active)·market 필터 동작 유지. 쓰기 엔드포인트(POST/PATCH/DELETE assets, 태그)는 `require_admin` 유지.
- 변경 2 — **사용자 가용 데몬 목록 read 엔드포인트 신규**: caller(user_id) 소유 active 데몬을 반환(id·daemon_slug·display_name·is_active). `require_read`, caller 스코프. 기존 서비스 메서드(`list_daemons_for_user`) 활용.
## 영향 범위
- 코드 영향: `app/api/v1_assets.py`(GET 권한 완화), 데몬 목록 라우트 신규(`app/api/`—경로 design), `app/services/auth_service.py`(기존 `list_daemons_for_user` 노출용 wiring), 응답 스키마(DaemonResponse 신규 가능).
- 문서 영향: `docs/srs/`(REQ), `docs/discovery/index.md`. API 계약 문서.
- 운영 영향: asset 카탈로그가 전 인증 사용자에게 read 공개됨(쓰기 불변). 배포 시 계약 변경 고지.
# 5. 요구사항 목록
## 기능 요구사항
- FR-1: `GET /v1/assets`를 `require_read`로 완화해 일반 사용자가 active asset 카탈로그를 조회할 수 있다. 기존 status/market 필터·응답 스키마 유지. asset 쓰기(생성/비활성/태그)는 `require_admin` 유지.
- FR-2: 사용자 가용 데몬 목록 read 엔드포인트를 추가한다 — `require_read`, caller(user_id) 소유 데몬(active) 반환. 응답 필드 id·daemon_slug·display_name·is_active.
## 비기능 요구사항
- NFR-1: 기존 admin asset 관리(쓰기)·서비스 토큰 daemon 바인딩 동작에 회귀 0.
- NFR-2: 데몬 목록은 caller 본인 데몬만 반환(타 사용자 데몬 비노출) — 권한/스코프 정확.
## 범위 경계
- In Scope
- GET /v1/assets require_read 완화
- 사용자 가용 데몬 목록 read 엔드포인트 신규
- Out of Scope
- asset 카탈로그 쓰기(생성/수정/비활성/태그) 권한 변경(admin 유지)
- 데몬 생성/수정/삭제 등 데몬 관리(이번엔 read 목록만)
- 포트폴리오 CRUD 자체(기존 존재) 및 trex-web UI(별도 trex-web dcy-007)
# 6. 리스크/가정 목록
## 리스크
- R-1 (Low): asset 카탈로그를 전 사용자 read 공개 — 민감 정보 노출 우려.
- 완화 방안: 카탈로그는 거래 가능 종목 메타(민감도 낮음). 쓰기는 admin 유지. 필요 시 status=active만 기본 노출.
- R-2 (Low): 데몬 목록 스코프 오류로 타 사용자 데몬 노출.
- 완화 방안: caller user_id 스코프 강제(`list_daemons_for_user(auth.user_id)`), 테스트로 타 사용자 비노출 검증.
## 가정
- A-1: 도메인 `Daemon`(owner_user_id)·`list_daemons_for_user`가 사용자 소유 데몬을 정확히 반환한다.
- A-2: asset 카탈로그(거래 종목 메타)는 전 인증 사용자에게 read 공개해도 무방하다(사장님 승인).
- A-3: 포트폴리오의 `managed_by_daemon_id`에 넣을 후보 = 사용자 소유 데몬(이 목록).
## 오픈 질문
- OQ-1: GET /v1/assets require_read 완화 — 승인 여부.
- 상태: **Answered (2026-06-27, 사장님)** — require_read로 완화 확정(쓰기는 admin 유지). FR-1 반영.
- OQ-2: 데몬 목록 엔드포인트 경로·형태 — 예: `GET /v1/daemons`(caller 스코프) vs `GET /v1/users/me/daemons`. 응답 스키마(DaemonResponse) 형태.
- 상태: Deferred
- 처리 방안: draft-batch-design에서 기존 라우팅 컨벤션에 맞춰 확정.
- 종료 조건: design에서 경로·스키마 확정 시 Answered.
- OQ-3: "가용 데몬" 정의 — 사용자 소유 active 데몬(`list_daemons_for_user`)으로 충분한지, 아니면 미바인딩 후보(`find_candidate_daemons_for_user`) 의미가 필요한지.
- 상태: Deferred
- 처리 방안: design에서 trex-web 포트폴리오 배정 요구(managed_by_daemon_id 후보)에 맞는 의미 1개 확정. 기본안 = 사용자 소유 active 데몬 전체.
- 종료 조건: design에서 의미 확정 시 Answered.
### 오픈 질문 처리 기준
- `Open`/`Answered`/`Deferred`/`Dropped` 표준 처리.
# 7. 초기 성공 기준
- S-1: 일반 사용자(read) 세션으로 `GET /v1/assets` 호출 시 200 + active 카탈로그 반환(admin 아님).
- S-2: 일반 사용자가 asset 쓰기(POST/PATCH/DELETE) 시 여전히 403(admin 전용 유지).
- S-3: 사용자 가용 데몬 목록 엔드포인트가 caller 소유 데몬만 반환(타 사용자 데몬 비노출).
- S-4: 기존 admin asset 관리·토큰 daemon 바인딩 회귀 0.
## 측정 방식 및 데이터 출처
- S-1/S-2: read·admin·다른권한 토큰별 통합 테스트(권한 매트릭스) / 데이터 출처: pytest.
- S-3: 2명 사용자 데몬 fixture로 스코프 테스트 / pytest.
- S-4: 기존 v1_assets·auth 테스트 회귀 / pytest.
# 8. REQ로 넘기기 전 확인 체크
- [x] 범위(In/Out) 확정 — read 2종 In, 쓰기·데몬관리·UI Out (사장님 합의)
- [x] 성공 지표 측정 방식 — 권한 매트릭스·스코프 테스트
- [x] High 리스크 없음(R-1/R-2 Low)
- [ ] OQ-2/OQ-3(엔드포인트 경로·가용데몬 의미) design 인계
- [x] Freeze 담당자(CONFIRMED_BY) — 팀장
# 9. 파일 처리 결과
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-009_20260627_user-read-asset-catalog-and-daemons.md
- 참조 문서:
- .github/templates/discovery/discovery.md (템플릿)
- app/api/v1_assets.py(GET /v1/assets — 완화 대상), app/domain/models.py(Daemon), app/services/auth_service.py(list_daemons_for_user)
- 발원: trex-web 포트폴리오 관리(예정 trex-web dcy-007)
# 10. 사용자 결정 필요 항목 요약
## DECIDE
- (해소됨, 2026-06-27) GET /v1/assets require_read 완화 — 승인.
## CONFIRM
- (해소됨, 2026-06-27) 백엔드 read 2종(asset 완화 + 사용자 데몬 목록) 단위 채택 — 쓰기·데몬관리·trex-web UI 제외. 백엔드 우선 진행.
## DATA
- (확인됨) Daemon 필드 id·owner_user_id·daemon_slug·display_name·is_active. asset GET 응답 = AssetCatalogListResponse.
# 11. Discovery Freeze
## 섹션 참조
- 범위: [# 5 > 범위 경계](#5-요구사항-목록) / 리스크·OQ: [# 6](#6-리스크가정-목록) / 성공: [# 7](#7-초기-성공-기준) / 인계 전 확인: [# 8](#8-req로-넘기기-전-확인-체크) / 결정: [# 10](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: REQ Drafting 진행 가능
- Handoff Rationale: 범위·asset 완화 승인 완료(사장님). OQ-2(엔드포인트 경로)·OQ-3(가용데몬 의미)만 design Deferred — REQ 인계 가능.
- Ready for REQ Drafting: true
- Confirmed By: 팀장 (Claude)
- Confirmed At (KST): 2026-06-27 10:07
- 비고: 본 Discovery는 사장님이 백엔드 변경(asset 완화 + 데몬 목록 read)을 명시 승인했으므로 작성과 동시에 confirm 처리. 상태는 confirm-discovery 게이트에서 confirmed로 전환.
# 12. 작성 가이드
- portfolio-hub .github/templates discovery 기반. trex-web dcy-007(UI)이 이 엔드포인트를 소비한다. design에서 OQ-2/3 확정.
+1
View File
@@ -14,3 +14,4 @@
| dcy-006_20260605_ledger-trade-write-portfolio-state-return | 20260605 | ledger trade write 회계 책임 단일화 — 응답에 portfolio 상태 포함 + portfolio_ledger/ledger_trades 테이블 통합 (issue #8) | [Discovery](./dcy-006_20260605_ledger-trade-write-portfolio-state-return.md) | confirmed |
| dcy-007_20260616_daily-trade-history-and-realized-pnl-query-api | 20260616 | 일일 거래내역 + 실현 PnL 조회 API (운영 리포트/프론트 데이터 소스, issue #11) | [Discovery](./dcy-007_20260616_daily-trade-history-and-realized-pnl-query-api.md) | confirmed |
| dcy-008_20260625_current-holding-wac-cost-basis-read | 20260625 | 현재 보유 종목별 평단(WAC) 조회 API — trex-core 중장기 cold-start 슬롯 평단 seed 의존(교차 발원 trex-core dcy-034 FR-7). GET /v1/portfolios/{id}/holdings/cost-basis | [Discovery](./dcy-008_20260625_current-holding-wac-cost-basis-read.md) | confirmed |
| dcy-009_20260627_user-read-asset-catalog-and-daemons | 20260627 | 사용자용 read 2종 — GET /v1/assets require_read 완화 + 사용자 가용 데몬 목록 신규(교차 발원 trex-web 포트폴리오 관리 dcy-007) | [Discovery](./dcy-009_20260627_user-read-asset-catalog-and-daemons.md) | confirmed |
@@ -0,0 +1,82 @@
# REQ-044: GET /v1/assets 권한 완화 — require_admin → require_read
- Status: Approved
- Approved By: 팀장 (Claude), 2026-06-27 (confirm-req)
- Type: Interface
- Priority: High
- Owner: Justin Ko
## Intent
- `GET /v1/assets`(asset 카탈로그 목록 조회) 엔드포인트의 의존성을 `require_admin`에서 `require_read`로 완화해, 일반 인증 사용자가 active asset 카탈로그를 조회할 수 있도록 한다.
- trex-web 포트폴리오 관리 UI(dcy-007)에서 포트폴리오 생성·변경 시 종목 선택 드롭다운/검색이 이 엔드포인트를 소비한다. 현재 `require_admin`으로 막혀 있어 일반 사용자가 접근 불가한 상태다.
- asset 쓰기(POST 생성, PATCH 수정/비활성, POST/DELETE 태그 적용/해제)는 `require_admin`을 유지한다 — 이번 완화는 읽기 하나에 한정한다.
## Requirement
- 시스템은 `GET /v1/assets` 엔드포인트의 인증 의존성을 `require_admin`에서 `require_read`로 변경해야 한다.
- 기존 동작(status 필터·market 필터·`AssetCatalogListResponse` 응답 스키마)은 변경 없이 유지해야 한다.
- asset 쓰기 엔드포인트(`POST /v1/assets`, `PATCH /v1/assets/{asset_id}`, `PATCH /v1/assets/{asset_id}/deactivate`, `POST /v1/assets/{asset_id}/tags`, `DELETE /v1/assets/{asset_id}/tags/{tag_id}`)는 `require_admin`을 유지해야 한다.
- 유효한 read 권한 이상 토큰(session, read service token, admin)으로 `GET /v1/assets`를 호출하면 HTTP 200과 카탈로그 목록을 반환해야 한다.
- 인증 토큰 없는 요청은 HTTP 401, 권한이 없는 토큰은 기존 동작대로 HTTP 403을 반환해야 한다.
## Acceptance Criteria
1. Given read 권한 세션 토큰(`permission="read"`)을 소지한 사용자, When `GET /v1/assets`를 호출, Then HTTP 200 + `AssetCatalogListResponse` 반환.
2. Given admin 토큰, When `GET /v1/assets`를 호출, Then 기존과 동일하게 HTTP 200 반환(회귀 없음).
3. Given read 권한 사용자, When `GET /v1/assets?status=active`·`?status=inactive`·`?status=all`로 호출, Then status 필터가 적용된 응답 반환(필터 동작 유지).
4. Given read 권한 사용자, When `GET /v1/assets?market=KRX`로 호출, Then market 필터가 적용된 응답 반환(필터 동작 유지).
5. Given read 권한 사용자, When `POST /v1/assets`(asset 생성)를 호출, Then HTTP 403(쓰기는 admin 유지).
6. Given read 권한 사용자, When `PATCH /v1/assets/{asset_id}/deactivate`를 호출, Then HTTP 403.
7. Given read 권한 사용자, When `POST /v1/assets/{asset_id}/tags`를 호출, Then HTTP 403.
8. Given read 권한 사용자, When `DELETE /v1/assets/{asset_id}/tags/{tag_id}`를 호출, Then HTTP 403.
9. Given 인증 토큰 없는 요청, When `GET /v1/assets`를 호출, Then HTTP 401.
## Impacted Area
- Docs: `docs/srs/Interface/req-044_get-v1-assets-require-read-relaxation.md` (신규)
- Modules:
- `app/api/v1_assets.py`: `list_catalog_assets` 함수의 `auth: AuthContext = Depends(require_admin)` → `Depends(require_read)` 변경. import에 `require_read` 추가.
- Tests: 기존 `tests/`의 `GET /v1/assets` 관련 테스트에서 read 토큰으로 200 반환하는 케이스 추가. 쓰기 엔드포인트 403 회귀 확인.
## Notes
- asset 카탈로그(거래 가능 종목 메타 — symbol·display_name·markets·status·태그)는 전 인증 사용자에게 read 공개해도 무방하다고 사장님이 확인(dcy-009 OQ-1 Answered 2026-06-27).
- 코드 변경 범위는 `app/api/v1_assets.py` 단일 파일의 `Depends(require_admin)` → `Depends(require_read)` 한 줄이다. 응답 로직·필터·스키마는 변경 없다.
- `require_read`는 `app/core/auth.py`에 이미 정의(`require_read = permission_dependency(Permission.READ)`)되어 있다.
- NFR-1(기존 admin asset 관리·토큰 daemon 바인딩 회귀 0)은 AC-2, AC-5~8의 쓰기 403 확인으로 흡수한다. 별도 NFR REQ 미생성.
- NFR-2(데몬 목록 caller 스코프)는 REQ-045에서 다룬다.
## Change Log
### CHG-20260627-01
- Date: 2026-06-27
- Author: Justin Ko
#### Change Summary
- 신규 생성 (Proposed) — dcy-009 FR-1 구현을 위한 첫 번째 REQ 초안.
#### Intent
- dcy-009 confirmed Discovery의 FR-1을 REQ로 정규화한다. 코드·테스트 구현의 기준 문서로 사용.
#### Acceptance Criteria Delta
- Added:
- AC-1 ~ AC-9 전체 (신규 초안)
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs:
- `docs/srs/Interface/req-044_get-v1-assets-require-read-relaxation.md` 신규 생성
- Modules:
- `app/api/v1_assets.py` (require_admin → require_read 1행)
- Tests:
- read 권한 GET 200 케이스 추가, 쓰기 403 회귀 케이스 확인
#### Delivery Trace Delta
- Batches: 미정 (suggest-batch-reqs 대기)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정
#### Validation Plan
- pytest로 read 토큰 GET /v1/assets 200 확인, admin 쓰기 403 회귀 확인.
@@ -0,0 +1,85 @@
# REQ-045: 사용자 가용 데몬 목록 read 엔드포인트 신설
- Status: Approved
- Approved By: 팀장 (Claude), 2026-06-27 (confirm-req)
- Type: Interface
- Priority: High
- Owner: Justin Ko
## Intent
- caller(인증 사용자)가 자신이 소유한 데몬 목록을 조회할 수 있는 HTTP 엔드포인트를 신설한다.
- trex-web 포트폴리오 관리 UI(dcy-007)에서 포트폴리오를 생성하거나 관리 데몬(`managed_by_daemon_id`)을 지정할 때, 사용자가 선택 가능한 데몬 후보를 이 엔드포인트로 불러온다.
- 현재 도메인 `Daemon` 모델과 `list_daemons_for_user(user_id)` 서비스/리포지토리가 존재하나 HTTP 라우트로 노출되지 않는 상태다. 이 REQ는 그 노출 경로를 추가한다.
## Requirement
- 시스템은 caller(인증 사용자) 소유 데몬 목록을 반환하는 GET 엔드포인트를 `require_read` 권한으로 제공해야 한다.
- 응답은 caller(`auth.user_id`) 소유 데몬만 포함해야 한다 — 타 사용자 데몬은 절대 반환하지 않는다.
- 각 응답 항목은 다음 필드를 포함해야 한다: `id`(str), `daemon_slug`(str), `display_name`(str | None), `is_active`(bool).
- 엔드포인트 경로와 응답 스키마(`DaemonResponse`) 구체 형태는 draft-batch-design에서 기존 라우팅 컨벤션에 맞춰 확정한다(OQ-2 Deferred).
- 인증 토큰 없는 요청은 HTTP 401을 반환해야 한다.
- 해당 사용자에게 데몬이 없는 경우 빈 목록(`[]`)으로 HTTP 200을 반환해야 한다(404 아님).
- 기존 서비스 메서드 `find_candidate_daemons_for_user(user_id)`(`AuthService`)를 활용한다. 이 메서드는 내부적으로 `repository.list_daemons_for_user(user_id)`를 호출한다.
## Acceptance Criteria
1. Given read 권한 토큰을 소지한 사용자 A(데몬 2개 소유), When 데몬 목록 엔드포인트를 호출, Then HTTP 200 + 사용자 A의 데몬 2개만 포함된 목록 반환.
2. Given read 권한 토큰을 소지한 사용자 A, When 데몬 목록 엔드포인트를 호출, Then 응답 항목이 `id`, `daemon_slug`, `display_name`, `is_active` 필드를 모두 포함.
3. Given read 권한 토큰을 소지한 사용자 A, When 데몬 목록 엔드포인트를 호출, Then 사용자 B의 데몬이 응답에 포함되지 않음(타 사용자 비노출).
4. Given read 권한 사용자이고 자신에게 데몬이 없을 때, When 데몬 목록 엔드포인트를 호출, Then HTTP 200 + 빈 목록(`items: []`).
5. Given 인증 토큰 없는 요청, When 데몬 목록 엔드포인트를 호출, Then HTTP 401.
6. Given admin 토큰을 소지한 사용자, When 데몬 목록 엔드포인트를 호출, Then HTTP 200 + 해당 admin 사용자 소유 데몬만 반환(admin 권한이어도 caller 스코프 적용).
7. Given read 권한 토큰으로 기존 `GET /v1/assets`(REQ-044)를 호출, Then 회귀 없음(데몬 라우터 추가가 기존 assets 라우터에 영향 없음).
## Impacted Area
- Docs: `docs/srs/Interface/req-045_user-daemon-list-endpoint.md` (신규)
- Modules:
- 신규 라우터 파일 또는 기존 라우터에 엔드포인트 추가(경로·파일 위치는 design 확정 — OQ-2).
- `app/schemas/v1.py` 또는 별도 스키마 파일: `DaemonResponse` Pydantic 모델 신규 정의.
- `app/services/auth_service.py`: `find_candidate_daemons_for_user` 기존 활용(변경 없거나 최소).
- `app/main.py` 또는 라우터 등록: 신규 라우터 include.
- Tests: 2사용자 fixture로 caller 스코프 격리 테스트(AC-3). 빈 목록·401 케이스. 기존 assets/auth 회귀.
## Notes
- OQ-2(엔드포인트 경로·응답 스키마 형태): design 단계에서 기존 라우팅 컨벤션(`/v1/daemons` vs `/v1/users/me/daemons` 등)을 분석해 확정. 이 REQ에서는 경로를 확정하지 않으며 design Deferred. confirm-req 시 blocker 아님 — design 단계 위임 가능.
- OQ-3("가용 데몬" 의미): 현재 기본안은 사용자 소유 데몬 전체(`find_candidate_daemons_for_user`). active 여부 필터링이 필요한지(소유 active 전체 vs 미바인딩 후보) design에서 확정. 이 REQ에서는 "caller 소유 데몬"으로만 명시하며 active 필터는 design 위임.
- env 기반 token(`token_source="env"`)은 `user_id`가 None이다. 이 경우 `auth.user_id`가 없어 caller 스코프 적용이 불가 — design에서 env 토큰의 처리 방침(거부 vs 전체 반환)을 확정할 것. 기본안: DB 토큰만 허용(user_id 없으면 HTTP 403).
- NFR-1(기존 admin asset 관리·토큰 daemon 바인딩 회귀 0)은 AC-7 회귀 확인으로 흡수한다.
- NFR-2(caller 스코프 정확)는 AC-1/3/6으로 흡수한다.
## Change Log
### CHG-20260627-01
- Date: 2026-06-27
- Author: Justin Ko
#### Change Summary
- 신규 생성 (Proposed) — dcy-009 FR-2 구현을 위한 REQ 초안.
#### Intent
- dcy-009 confirmed Discovery의 FR-2를 REQ로 정규화한다. 코드·테스트 구현의 기준 문서로 사용.
#### Acceptance Criteria Delta
- Added:
- AC-1 ~ AC-7 전체 (신규 초안)
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs:
- `docs/srs/Interface/req-045_user-daemon-list-endpoint.md` 신규 생성
- Modules:
- 신규 라우터 파일(경로 design 확정 후), `DaemonResponse` 스키마 신규, `app/main.py` 라우터 등록
- Tests:
- 2사용자 스코프 격리 테스트, 빈 목록·401 케이스 신규
#### Delivery Trace Delta
- Batches: 미정 (suggest-batch-reqs 대기)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. OQ-2/OQ-3는 design 단계에서 해소.
#### Validation Plan
- pytest로 AC-1~7 각 케이스 확인. 특히 AC-3(2사용자 fixture 사용해 타 사용자 데몬 비노출) 강제 검증.
+4 -1
View File
@@ -74,7 +74,7 @@
## Next Requirement ID
- Current: `REQ-044`
- Current: `REQ-046`
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
## Requirement Register
@@ -124,9 +124,12 @@
| REQ-041 | WAC 기반 cost_basis 산출 및 실현 PnL 계산 로직 (service/domain 계층 격리) | Integration | Implemented | High | Justin Ko | [req-041](./Integration/req-041_wac-cost-basis-pnl-calculation.md) |
| REQ-042 | 체결 내역/실현 PnL API 테스트 커버리지 (WAC 시나리오 기댓값 대조, 날짜 필터, 422) | Testing | Implemented | High | Justin Ko | [req-042](./Testing/req-042_trade-pnl-api-test-coverage.md) |
| REQ-043 | GET /v1/portfolios/{id}/holdings/cost-basis 현재 보유 평단(WAC) read 엔드포인트 신설 | Interface | Implemented | High | Justin Ko | [req-043](./Interface/req-043_holdings-cost-basis-read-endpoint.md) |
| REQ-044 | GET /v1/assets 권한 완화 — require_admin → require_read | Interface | Approved | High | Justin Ko | [req-044](./Interface/req-044_get-v1-assets-require-read-relaxation.md) |
| REQ-045 | 사용자 가용 데몬 목록 read 엔드포인트 신설 | Interface | Approved | High | Justin Ko | [req-045](./Interface/req-045_user-daemon-list-endpoint.md) |
## Recent Change Log Summary
- 2026-06-27: REQ-044/045 신규 (Proposed) — dcy-009 FR-1(GET /v1/assets require_read 완화)·FR-2(사용자 가용 데몬 목록 엔드포인트 신설) REQ 초안 작성. 엔드포인트 경로·스키마(OQ-2) 및 가용 데몬 의미(OQ-3)는 design Deferred.
- 2026-06-26: REQ-041 WAC 공식 change-req(CHG-20260626-01) — 매수 수수료 cost basis 편입(사장님 결정). 신 공식: `running_wac = (running_wac*qty + price*qty + buy_fee) / new_total_qty`. `calculate_realized_pnl`과 `calculate_current_wac` 양 함수 적용. AC-9/AC-10 추가. 신규 테스트 9개 포함 456 passed / 0 failed. REQ-040/REQ-043 Notes 참조 갱신.
- 2026-06-26: REQ-043 `Approved` → `Implemented` — BAT-018 PR #14 squash-merge(d93c0bc) 팀장 QC PASS 후 origin/main 머지 완료. AC-1~11 전부 PASS(447 passed / 0 failed). 배포 별도(release-candidate).
- 2026-06-25: REQ-043 `Proposed` → `Approved` (confirm-req 팀장 승인) — dcy-008 근거 Discovery 양방향 링크 확정. OQ-1(service token 소유권 blocker) 해소: cold-start는 `managed_by_daemon_id=daemon` 포트폴리오만 조회하므로 null 케이스 blocker 아님, daemon scope 소유권 정책(REQ-029 선례) 유지 확정. OQ-2(domain 함수) 구현 단계 재량 위임. AC-1~11 유지.