BAT-006 (REQ-016/017/018): - client token 발급 경로 제거 (issue_client_token, POST /auth/tokens, 관련 스키마/테스트) - service-token rotate unique index revoked_at IS NULL 조건 추가 (Alembic CONCURRENTLY 마이그레이션) - POST /auth/service-tokens body daemon_id forward 누락 보정 (1-line fix) BAT-007 (REQ-022/023): - daemon deactivate 시 바인딩 service token 자동 revoke (CLI --force 옵션 + revoke_tokens_by_daemon_id) - NULL daemon_id legacy token 진단 CLI (migrate-null-daemon --dry-run) 거버넌스 문서: dcy-003/004 Discovery, REQ-016~023/025~032 신규, BAT-006/007 전체 산출물 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
305 lines
47 KiB
Markdown
305 lines
47 KiB
Markdown
# 0. 문서 상태
|
|
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
|
|
|
|
- 상태: confirmed
|
|
- 문서 ID: dcy-003_20260520_user-portfolio-mutation-v1-api-migration
|
|
- 이슈명: User Portfolio Mutation v1 API 이전 및 path 명명 분리 (/v1/portfolios* 신설)
|
|
- 작성 시각(KST): 2026-05-20T00:00+09:00
|
|
- 마지막 갱신 시각(KST): 2026-05-20T15:00+09:00 (draft-req 완료 — REQ-025~032 8건 생성. FR-1+2 통합, FR-3/4/5/6/7/8/10 각 분리, FR-9는 REQ-013 Deprecated로 처리 완료라 별도 REQ 없음, NFR-1~4 흡수, docs/srs/index.md 갱신)
|
|
- 대체됨: 없음
|
|
- 후속 Discovery 참조: docs/discovery/dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md (client token 발급 경로 폐지 + 토큰 surface 정비, 2026-05-20 confirmed)
|
|
- 생성된 REQ 참조: REQ-025, REQ-026, REQ-027, REQ-028, REQ-029, REQ-030, REQ-031, REQ-032
|
|
|
|
# 1. 계획 상태 요약
|
|
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
|
|
|
|
- 연계 문서 현황:
|
|
- 관련 점수 파일: 미생성
|
|
- 관련 TODO/메모: temp/2026-05-18_todo.md (구현 결함 inventory — `POST /v1/assets/{asset_id}/ledger/trades`의 path param 의미 불일치 항목이 본 Discovery로 해소될 가능성)
|
|
- 해석:
|
|
- dcy-001(asset catalog 분리, confirmed)이 `# 5. 요구사항 목록 > 범위 경계 > Out of Scope`에서 "user portfolio mutation HTTP API ... 별도 Discovery에서 `/v1/` 경로로 이전 예정"을 명시했고, 본 Discovery가 그 후속에 해당한다. dcy-001 FR-2/FR-5가 정의한 `/v1/assets` path 위에 portfolio CRUD를 더 얹는 대신, path를 `/v1/portfolios*`로 완전 분리(옵션 B)하여 catalog와 포지션의 의미를 갈라낸다.
|
|
- 본 Discovery는 dcy-001 FR-5의 "GET /v1/assets = portfolio 조회 endpoint" 결정을 명시적으로 override한다. dcy-001 REQ-009~015는 2026-05-19 일괄 Approved, REQ-013(FR-5 산출물)은 본 Discovery 확정(2026-05-20) 후 change-req(CHG-20260520-01)로 Deprecated 처리 완료. 본 Discovery REQ 작성을 위한 선행 조건은 모두 충족됨.
|
|
|
|
# 2. 요구사항 판정 결과
|
|
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
|
|
|
|
- 판정: 신규
|
|
- 근거
|
|
- 현재 활성 SDLC(docs/discovery/, docs/srs/) 어디에도 user portfolio mutation HTTP API 요구사항이 존재하지 않으며, dcy-001 Out of Scope가 "별도 Discovery에서 처리" 명시 예고를 남겼다.
|
|
- `temp/20260519_old_sdlc/discovery/dcy-007_20260518_v1-asset-crud-endpoints.md`는 폐기된 옛 SDLC의 draft 잔재로 active 후보가 아니다. 본 Discovery는 dcy-001(catalog 분리)과 dcy-002(tag_ids 정책)로 재정의된 새 데이터 모델 위에서 path 명명을 분리해 재구성하므로 기존 어떤 주기와도 중복되지 않는다.
|
|
|
|
# 3. 문제점의 요약
|
|
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
|
|
|
|
- 현재 상태
|
|
- v1 API에 portfolio mutation 경로가 존재하지 않는다. `app/api/v1_assets.py`(GET /v1/assets, daemon-scoped 조회), `app/api/v1_user_info.py`(GET /v1/user_info), `app/api/v1_ledger.py`(POST /v1/assets/{asset_id}/ledger/trades) 3종만 라우터에 등록되어 있다.
|
|
- `POST /v1/assets/{asset_id}/ledger/trades`의 path parameter 이름은 `asset_id`이나 실제 값은 `portfolios.portfolio_id`(UUID)로, asset과 portfolio의 의미가 한 path에 혼용된다.
|
|
- dcy-001 FR-5는 `GET /v1/assets`를 "토큰 타입별 통합 portfolio 조회"로 재정의했지만, 같은 path가 dcy-001 FR-2 정의의 catalog CRUD(`POST/PATCH /v1/assets`)와 포지션 조회 두 의미를 동시에 가져 응답 schema가 토큰 타입별로 갈라진다.
|
|
- portfolio 생성/수정/삭제/매수/매도/입출금/이체/태그/코멘트의 도메인 로직은 `app/services/portfolio_service.py`(`create_portfolio`, `update_portfolio`, `delete`, `buy`, `sell`, `invest`, `transfer`, `add_tag`, `remove_tag`, `create_comment`, `update_comment`, `delete_comment` 등)에 이미 구현되어 있으나 v1 라우터로 노출되지 않는다. CLI(`pfhub portfolio ...`)와 비-v1 `app/api/portfolios.py` 라우트만 해당 기능을 노출한다.
|
|
- baseline 문서(`docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/data-model.md`, `docs/project-structure.md`)는 모두 존재하며 dcy-001/002 변경 사항을 아직 반영하지 않은 직전 baseline 상태다.
|
|
- 기대 상태
|
|
- `/v1/assets*` = admin asset catalog 전용 경로(dcy-001 FR-2 정의 범위만 유지).
|
|
- `/v1/portfolios*` = user/daemon/admin이 portfolio(포지션)를 CRUD하고 ledger·tags·comments를 다루는 경로 집합.
|
|
- `GET /v1/portfolios`는 토큰 타입별 분기 조회(admin: 본인 또는 `?user_id=<id>`로 타 user, user: 본인, daemon: `managed_by_daemon_id` 필터 + `?market` 지원).
|
|
- ledger 기록은 `POST /v1/portfolios/{portfolio_id}/ledger/trades`로 이전, path param 이름과 의미가 일치한다.
|
|
- portfolio 생성 시 dcy-001 catalog 검증(`asset_id`가 `status='active'`인지 확인) 적용, portfolio 태그 적용 시 dcy-002 tag_ids 정책(is_admin_tag=TRUE 또는 소유자 생성 태그) 준수.
|
|
- 이해관계자
|
|
- admin: portfolio HTTP CRUD를 통해 본인 또는 타 user의 포지션을 프로비저닝하는 운영자
|
|
- 로그인 user: 자신의 portfolio를 HTTP로 생성/수정/삭제/거래하려는 사용자
|
|
- daemon (trex-basic): 배정된 portfolio에 대해 조회·ledger 기록을 수행하는 서비스 에이전트. 본 Discovery 시점에 v1 path가 `/v1/portfolios*`로 변경되어 클라이언트 코드 수정이 필요하다
|
|
- repository-maintainers: 본 Discovery의 REQ 작성·구현·검증 담당
|
|
- 제약사항
|
|
- dcy-001 FR-5 결정을 override → dcy-001 REQ-013(Approved)에 change-req 처리 완료(2026-05-20). REQ-013 Status → Deprecated, dcy-001 §0 후속 Discovery 참조 갱신, REQ-010 Notes에 GET /v1/assets catalog 명세 처리 계획 추가.
|
|
- 선행 조건 충족: dcy-001 REQ-009~015 모두 Approved 상태(2026-05-19 일괄 승인). 본 Discovery REQ 작성은 즉시 가능.
|
|
- dcy-002 portfolios.tag_ids 정책(is_admin_tag 또는 소유자 검증) 준수
|
|
- trex-basic은 본 batch와 동시 배포 필요(한시적 별칭 운영 없이 — OQ-3 Answered 2026-05-20). 구 path는 동일 release에서 제거된다.
|
|
- 인증 모델은 현행 Bearer token 체계(env token 또는 DB user/service token + Permission 체계 ADMIN/TRADE_WRITE/READ)를 그대로 사용한다
|
|
|
|
## 주요 사용자 시나리오
|
|
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
|
|
|
|
- admin이 `POST /v1/portfolios`에 body `{"asset_id": "<catalog-uuid>", "broker": "kiwoom", "cash_balance": 1_000_000, "user_id": "alice-uuid"}`로 요청해 alice의 portfolio를 프로비저닝한다. asset_id가 catalog에 `status='active'`로 존재하지 않으면 거부된다.
|
|
- alice가 로그인으로 발급받은 session token으로 `PATCH /v1/portfolios/{portfolio_id}`로 본인 portfolio의 `strategy_name`을 변경한다. bob의 token으로 alice의 portfolio를 수정하려 하면 HTTP 403을 받는다.
|
|
- alice가 `DELETE /v1/portfolios/{portfolio_id}`로 본인 portfolio를 종료한다. portfolio는 `status='disabled'`로 전환되고 `managed_by_daemon_id`는 자동 해제되며, ledger·comments·tags는 보존된다(soft delete, OQ-1 Answered 2026-05-20).
|
|
- daemon(service-token, daemon-bound)이 `GET /v1/portfolios?market=KRX`를 호출해 KRX 시장 활성 자산에 매핑되고 자신에게 배정된 portfolio만 받는다.
|
|
- daemon이 체결 발생 시 `POST /v1/portfolios/{portfolio_id}/ledger/trades`로 idempotent하게 거래 기록을 등록한다.
|
|
- user가 `POST /v1/portfolios/{portfolio_id}/tags` body `{"tag_id": "<uuid>"}`로 태그를 적용한다. 태그가 admin 생성이 아니고 소유자가 아니라면 HTTP 403(dcy-002 FR-3 정책)을 받는다.
|
|
|
|
# 4. 이번에 정의할 변경
|
|
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
|
|
|
|
- `/v1/portfolios*` 신설 — POST/GET/PATCH/DELETE 및 `/ledger/trades`·`/tags`·`/comments` 서브 라우트 일괄 구현. 단일 라우트에서 토큰 타입(admin/user/daemon) 분기로 authorization 처리.
|
|
- `GET /v1/assets`의 portfolio 조회 의미 회수 → catalog 전용으로 복원. dcy-001 FR-5 산출물 REQ-013에 change-req(CHG-20260520-01) 적용 완료(Status → Deprecated, 2026-05-20).
|
|
- `POST /v1/assets/{asset_id}/ledger/trades`를 `POST /v1/portfolios/{portfolio_id}/ledger/trades`로 이전, path param 이름과 의미를 일치시킴. 동일 batch에서 구 path 제거.
|
|
- portfolio 생성 시 dcy-001 catalog 검증(asset_id `status='active'`) 통합, portfolio tag 적용 시 dcy-002 tag_ids 정책 준수.
|
|
- baseline 문서 갱신: `docs/interface-contract.md`, `docs/runtime-flows.md` (path 분리, daemon authorization, catalog 검증 흐름 반영)
|
|
|
|
## 영향 범위
|
|
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
|
|
|
|
- 코드 영향:
|
|
- `app/api/v1_portfolios.py`: 신규 파일 — `/v1/portfolios*` 라우트 전체 (CRUD + ledger + tags + comments). 토큰 타입 분기 인증 헬퍼(`_require_portfolio_access`) 포함.
|
|
- `app/api/v1_assets.py`: GET 동작에서 portfolio 조회 로직 제거, catalog 전용으로 회수. dcy-001 FR-2의 admin catalog CRUD는 유지.
|
|
- `app/api/v1_ledger.py`: 신규 path(`/v1/portfolios/{portfolio_id}/ledger/trades`)로 라우트 prefix 이전하거나 `v1_portfolios.py`에 흡수. 구 path는 제거.
|
|
- `app/main.py`: v1 라우터 include 목록 갱신.
|
|
- `app/schemas/v1.py`: `PortfolioCreateRequest`, `PortfolioUpdateRequest`, `PortfolioItem`, `PortfolioListResponse`, `PortfolioTagApplyRequest`, `PortfolioCommentRequest` 등 신규 schema. 기존 `AssetItem`/`AssetsResponse`는 catalog 응답 의미로 회수 또는 분리.
|
|
- `app/services/portfolio_service.py`: 기존 메서드 재사용. `create_portfolio`에 catalog 검증(asset_id active 확인) 추가.
|
|
- `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`: 신규 메서드 도입 없음 또는 catalog 검증용 read-path만 추가. 기존 mutation 메서드 재사용.
|
|
- `app/api/portfolios.py`(legacy 비-v1 `/portfolios/*` 라우트): 본 Discovery 범위 밖. 제거 시점은 후속 Discovery로 분리.
|
|
- 문서 영향:
|
|
- `docs/interface-contract.md`: `/v1/portfolios*` 명세 추가, `/v1/assets*`의 의미 회수 반영, daemon authorization model 갱신
|
|
- `docs/runtime-flows.md`: portfolio mutation flow, catalog 검증 flow, /v1/portfolios path 명명 분리 정책, ledger path 이전 흐름
|
|
- `docs/discovery/dcy-001_20260519_asset-catalog-and-user-portfolio-model.md`: §0 후속 Discovery 참조 → dcy-003 추가 완료(2026-05-20), 생성된 REQ 참조에 REQ-013 Deprecated 표기. REQ-013(`docs/srs/Interface/req-013_get-v1-assets-token-branching.md`)은 Deprecated 전환 + Change Log CHG-20260520-01 기록. REQ-010 Notes는 GET /v1/assets catalog 명세 처리 계획 보강 완료.
|
|
- `docs/project-structure.md`: `app/api/v1_portfolios.py` 신규 파일 추가 및 라우터 책임 갱신 — 필요 시 baseline 갱신
|
|
- 운영 영향:
|
|
- trex-basic은 본 batch release window에 새 path로 동시 배포 필요. release plan에 동시 배포 절차와 verification checklist 명시.
|
|
- admin이 HTTP로 타 user portfolio를 생성/삭제할 수 있게 되므로 admin token 발급·관리 절차에 권한 영향 반영
|
|
- DELETE 방식: soft delete로 확정(OQ-1 Answered 2026-05-20). 운영 SOP — portfolio status='disabled' 전환 + admin이 `PATCH status='enabled'`로 복구, 의존 데이터(ledger·comments·tags) 보존, 누적 disabled 정리는 별도 운영 Discovery로 위임
|
|
|
|
# 5. 요구사항 목록
|
|
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
|
|
|
|
## 기능 요구사항
|
|
- FR-1: `POST /v1/portfolios` 신설 — Bearer 인증 후 portfolio 생성. body 필수 필드: `asset_id`, `broker`. 선택 필드: `name`, `cash_balance`, `asset_quantity`, `asset_price`, `profit_buffer`, `strategy_name`, `strategy_params`, `managed_by_daemon_id`, `user_id`. **토큰 분기 규칙 (OQ-2, OQ-5 Answered 2026-05-20)**:
|
|
- (1) `token_type ∈ {service, client}` → HTTP 403 fail-closed. `service`는 daemon-bound이며 portfolio CRUD 주체가 아님(OQ-2). `client`는 발급 경로 자체가 [dcy-004](./dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md)(confirmed 2026-05-20)에서 폐지 확정. 본 Discovery는 폐지 release 이전 운영에서도 v1 portfolio mutation 진입을 사전 차단해 일관성 유지(OQ-5).
|
|
- (2) 그 외(`token_type=session` 또는 env token, `token_source` 무관):
|
|
- `permission='admin'` → admin 분기. body `user_id` non-null이면 해당 user의 portfolio로, null이면 인증 주체의 user_id로 생성.
|
|
- `permission ∈ {read, trade-write}` → user 분기. 인증 주체의 user_id로 생성. body `user_id` 지정은 무시 또는 HTTP 422.
|
|
- env admin token과 admin permission을 가진 session token은 admin 분기에서 동일하게 처리된다.
|
|
- FR-2: portfolio 생성 시 `asset_id`가 dcy-001 catalog의 `assets` 테이블에 존재하고 `status='active'`인지 검증한다. 미등록 또는 inactive면 HTTP 422(요청 검증) 또는 403(권한/정책) 중 dcy-001 FR-4와 일관된 응답 코드로 거부한다. 검증 위치(서비스 계층 공통 진입점 vs v1 라우터 레이어 한정)는 REQ 작성 단계에서 결정한다 — 공통 서비스 진입점에 두면 CLI 회귀 영향이 있고(R-5), 라우터 레이어에 두면 CLI는 무영향이나 검증 중복이 발생한다.
|
|
- FR-3: `PATCH /v1/portfolios/{portfolio_id}` 신설 — Bearer 인증 후 부분 수정. **토큰 분기 규칙 (OQ-2, OQ-5 Answered 2026-05-20, FR-1과 동일 패턴)**:
|
|
- (1) `token_type ∈ {service, client}` → HTTP 403 fail-closed.
|
|
- (2) 그 외(`token_type=session` 또는 env token):
|
|
- `permission='admin'` → 모든 portfolio 수정 가능.
|
|
- `permission ∈ {read, trade-write}` → 자기 소유(`owner_user_id = auth.user_id`) portfolio만 수정 가능, 불일치 시 HTTP 403.
|
|
- 비존재 portfolio는 HTTP 404. 수정 가능 필드는 기존 `UpdatePortfolioRequest` 범위(`broker`, `status`, `name`, `symbol`, `strategy_name`, `strategy_params`, `asset_price`, `managed_by_daemon_id` 등).
|
|
- FR-4: `DELETE /v1/portfolios/{portfolio_id}` 신설 — Bearer 인증 후 portfolio를 **soft delete**한다(OQ-1 Answered 2026-05-20). **토큰 분기 규칙 (OQ-2, OQ-5 Answered 2026-05-20, FR-1과 동일 패턴)**:
|
|
- (1) `token_type ∈ {service, client}` → HTTP 403 fail-closed.
|
|
- (2) 그 외(`token_type=session` 또는 env token):
|
|
- `permission='admin'` → 모든 portfolio 삭제 가능.
|
|
- `permission ∈ {read, trade-write}` → 자기 소유 portfolio만 삭제 가능, 불일치 시 HTTP 403.
|
|
- 동작: `portfolios.status = 'disabled'`로 전환, `managed_by_daemon_id`는 `NULL`로 자동 해제, `updated_at` 갱신. 의존 데이터(`portfolio_ledger`, `ledger_trades`, `portfolio_comments`, `portfolios.tag_ids`)는 모두 보존한다. disabled 상태 portfolio는 다음 write/도메인 동작이 차단된다: `PATCH`(status 전이 외 필드 변경), `POST /ledger/trades`, tag/comment 추가·수정·삭제. disabled portfolio는 `GET /v1/portfolios` 기본 응답에서 제외하되 `?include_disabled=true`로 조회 가능. 복구는 admin이 `PATCH /v1/portfolios/{portfolio_id}`로 `status='enabled'`(또는 그 전 status)로 전환하면 가능하며, daemon 재배정은 별도 호출로 수행한다. hard delete 경로는 v1에 노출하지 않는다. 비존재 portfolio는 HTTP 404, 이미 `disabled` 상태인 portfolio에 재호출 시 HTTP 200 + 멱등 응답(또는 HTTP 409 — REQ 단계에서 응답 코드 확정).
|
|
- FR-5: `GET /v1/portfolios` 신설 — 토큰 분기 조회 (dcy-001 FR-5의 정의를 path만 변경해 흡수). **토큰 분기 규칙 (OQ-5 Answered 2026-05-20)**:
|
|
- `token_type='service'`: daemon-bound 조회. `daemon_id is None`이면 HTTP 403 fail-closed. 그 외 `managed_by_daemon_id = auth.daemon_id` 필터 + `?market=<code>` 지원.
|
|
- `token_type='client'`: HTTP 403 fail-closed ([dcy-004](./dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md) confirmed 2026-05-20 — 발급 경로 폐지 확정, OQ-5).
|
|
- `token_type ∈ {session, None(env)}`:
|
|
- `permission='admin'` → 기본 `owner_user_id = auth.user_id`로 본인 portfolio 반환. `?user_id=<id>` 지정 시 해당 user의 portfolio 반환.
|
|
- `permission ∈ {read, trade-write}` → `owner_user_id = auth.user_id` 필터, 본인 portfolio만 반환.
|
|
- 응답 항목의 `asset_id`는 dcy-001 catalog UUID 참조. 항목 schema는 새 `PortfolioItem`으로 정의.
|
|
- FR-6: `POST /v1/portfolios/{portfolio_id}/ledger/trades` 신설 — 기존 `POST /v1/assets/{asset_id}/ledger/trades` 동작을 path만 변경해 그대로 이전. daemon assignment 3단계 fail-closed 인가(token daemon_id NULL → 403, asset 미배정 → 403, 불일치 → 403), idempotency(uq_ledger_trades_idempotency), duplicate/conflict 응답(HTTP 200 status="duplicate" / HTTP 409) 동일 유지. 동일 batch에서 구 path는 제거되어 호출 시 HTTP 404.
|
|
- FR-7: portfolio tag 적용/해제/조회 — `POST /v1/portfolios/{portfolio_id}/tags` (body: `{"tag_id": "<uuid>"}`), `DELETE /v1/portfolios/{portfolio_id}/tags/{tag_id}`, `GET /v1/portfolios/{portfolio_id}/tags`. dcy-002 FR-3 정책 준수: `tags.is_active=TRUE`이고 `tags.is_admin_tag=TRUE` 또는 `tags.created_by_user_id = portfolio.owner_user_id`인 경우에만 적용 허용. 정책 위반 시 HTTP 403. ownership 규칙은 FR-3과 동일.
|
|
- FR-8: portfolio comment CRUD — `POST/GET /v1/portfolios/{portfolio_id}/comments`, `PATCH/DELETE /v1/portfolios/{portfolio_id}/comments/{comment_id}`. ownership 규칙은 FR-3과 동일. 기존 `CommentCreateRequest`/`CommentUpdateRequest`/`CommentResponse` schema 재사용 또는 v1 전용 래핑.
|
|
- FR-9: dcy-001 FR-5의 "GET /v1/assets = portfolio 조회 endpoint" 결정을 회수한다. `GET /v1/assets`는 dcy-001 FR-2의 admin catalog 조회 전용 의미로 복원. **dcy-001 FR-5 산출물 REQ-013에 change-req CHG-20260520-01 적용 완료(2026-05-20)** — Status → Deprecated, dcy-001 §0 후속 Discovery 참조 갱신, REQ-010 Notes 보강. `GET /v1/assets` catalog 조회 명세는 dcy-001 batch design 단계에서 REQ-010 보강 또는 신규 REQ로 처리 예정.
|
|
- FR-10: baseline 문서(`docs/interface-contract.md`, `docs/runtime-flows.md`) 갱신 — `/v1/portfolios*` 신설 명세, `/v1/assets*` 의미 회수, ledger path 이전, daemon authorization model 갱신, portfolio create flow에 catalog 검증 단계 추가.
|
|
|
|
## 비기능 요구사항
|
|
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
|
|
|
|
- NFR-1: 모든 `/v1/portfolios*` 엔드포인트는 Bearer token 필수. 미인증 요청은 HTTP 401, 권한 부족은 HTTP 403, 비존재 자원은 HTTP 404로 통일한다.
|
|
- NFR-2: trex-basic과 portfolio-hub는 본 batch release window에서 **동시 배포된다(OQ-3 Answered 2026-05-20 — 한시적 별칭/redirect 채택하지 않음)**. 구 `/v1/assets`의 portfolio 조회 의미와 구 `POST /v1/assets/{asset_id}/ledger/trades` path는 동일 release에서 제거되며, 신 path(`/v1/portfolios*`)만 단일 서빙된다. release plan에 동시 배포 절차, 배포 순서(예: trex-basic 클라이언트 변경 사전 빌드 완료 → portfolio-hub 배포 → trex-basic 배포의 시점 잠금), rollback 조건(portfolio-hub 단독 rollback 시 trex-basic 영향 격리 방법)을 명시한다.
|
|
- NFR-3: authorization 분기는 라우터 레이어의 토큰 타입(admin/user/daemon) 분기로 단일 라우트 내에서 처리한다. principal별로 별도 라우트(예: `/v1/admin/portfolios`)를 신설하지 않는다.
|
|
- NFR-4: 기존 `app/services/portfolio_service.py`의 메서드 시그니처와 동작은 catalog 검증 통합 외 변경을 최소화한다. 서비스 계층 회귀를 줄이고 라우터 레이어 추가에 집중한다.
|
|
|
|
## 범위 경계
|
|
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
|
|
|
|
- In Scope
|
|
- `/v1/portfolios*` 라우트 신설: CRUD + ledger + tags + comments
|
|
- `/v1/assets`의 portfolio 조회 의미 회수 (catalog 전용 복원)
|
|
- `POST /v1/assets/{asset_id}/ledger/trades` → `POST /v1/portfolios/{portfolio_id}/ledger/trades` 이전 및 구 path 제거
|
|
- portfolio 생성 시 dcy-001 catalog 검증 통합
|
|
- portfolio tag 적용 시 dcy-002 tag_ids 정책 준수
|
|
- baseline 문서 갱신 (interface-contract.md, runtime-flows.md)
|
|
- dcy-001 FR-5 change-req 등록 — REQ-013 Deprecated 처리 완료(2026-05-20)
|
|
- trex-basic 동시 배포 운영 계획 수립 (절차 상세는 NFR-2 source of truth)
|
|
- Out of Scope
|
|
- 비-v1 `app/api/portfolios.py`의 legacy `/portfolios/*` 라우트 제거 — 후속 Discovery로 분리
|
|
- 사용자 로그인/세션 시스템 신설 — 현행 Bearer token 체계 유지
|
|
- 매수/매도/입출금/이체 등 거래 동사(`buy`, `sell`, `invest`, `transfer`)의 v1 HTTP 노출 — OQ-4 결정으로 본 Discovery 범위 밖 확정(2026-05-20). v1에서는 `POST /v1/portfolios/{portfolio_id}/ledger/trades`(idempotency key 기반 ledger 기록)만 제공하며, 거래 동사는 현행 CLI(`pfhub portfolio buy/sell/invest/transfer`)로 계속 운용한다. HTTP 노출 필요성이 실증되면 후속 Discovery에서 거래 의미론(사후 기록 vs broker 호출 위임)과 daemon ledger 경로 책임 분리를 별도 정의한 뒤 추가한다.
|
|
- daemon(service-token)이 portfolio를 v1 API로 직접 생성·수정·삭제하는 경로 — OQ-2 결정으로 거부 확정(HTTP 403). daemon의 v1 권한은 조회(GET /v1/portfolios)와 ledger 기록(POST /v1/portfolios/{id}/ledger/trades)에 한정한다.
|
|
- trex-basic 클라이언트 코드 수정 — portfolio-hub 측 release plan에 동시 배포만 포함, trex-basic 측 변경은 해당 저장소 작업
|
|
|
|
# 6. 리스크/가정 목록
|
|
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
|
|
|
|
## 리스크
|
|
- R-1 (High): trex-basic 동시 배포(NFR-2) 실패 시 trex-basic이 구 `/v1/assets`(포지션 조회) 또는 구 `POST /v1/assets/{asset_id}/ledger/trades`를 계속 호출해 HTTP 404가 발생하고 거래 기록이 차단되는 운영 장애가 발생할 수 있다. OQ-3에서 한시적 별칭을 채택하지 않기로 결정했으므로 동시 배포 절차의 정확성이 본 리스크의 유일한 방어선이다.
|
|
- 완화 방안: (a) release plan에 trex-basic 배포 의존성과 배포 순서 잠금(trex-basic 신 path 클라이언트 빌드 사전 완료 → portfolio-hub 배포 → trex-basic 배포)을 명시. (b) batch verification 단계에서 trex-basic의 신 path 호출 evidence(통합 smoke test 또는 staging 환경 dry-run)를 확보. (c) trex-basic 측 클라이언트가 양방향 호환(구·신 path 둘 다 시도) 가능하도록 일시 코드를 둘지 여부는 trex-basic 저장소 책임으로 분리 — portfolio-hub 측에서는 신 path 단일 서빙. (d) rollback 시나리오: portfolio-hub 단독 rollback이 trex-basic을 깨뜨리므로, rollback은 trex-basic 동반 rollback을 전제로 release plan에 명시한다.
|
|
- R-2 (Medium): DELETE를 soft delete로 결정(OQ-1 Answered)했으므로 hard delete로 인한 데이터 손실 리스크는 해소되었으나, `status='disabled'` portfolio가 누적되면 운영 가시성 저하와 `GET /v1/portfolios` 기본 응답 필터링 회귀 가능성이 남는다. 또한 disabled 상태에서 write 차단 경계(ledger trade·tag·comment 거부)가 누락되면 데이터 일관성이 깨질 수 있다.
|
|
- 완화 방안: disabled portfolio는 `GET /v1/portfolios` 기본 응답에서 제외하고 `?include_disabled=true` opt-in으로만 노출(FR-5 보강 필요 — REQ 단계에서 명시). 모든 write/도메인 동작 경로(`PATCH` status 외 필드, ledger trade, tag/comment 변경)에 disabled guard를 일관 적용하고 회귀 테스트로 검증한다. 누적 disabled 정리(아카이브, 영구 삭제 운영 SOP)는 별도 운영 Discovery로 위임한다.
|
|
- R-3 (해소됨, 잔여 모니터링 Low): dcy-001 FR-5 → REQ-013(Approved)에 대한 change-req 처리는 2026-05-20에 REQ-013 Deprecated 전환으로 완료. dcy-001 batch 일정 영향은 REQ-010 보강(GET /v1/assets catalog 명세) 작업이 batch design 단계에 추가되는 정도로 한정. 잔여 리스크는 REQ-010 보강 작업의 batch 편입 시점이 dcy-001 batch 일정을 지연시킬 가능성.
|
|
- 완화 방안: dcy-001 batch planning 시 REQ-010 보강 작업(또는 GET /v1/assets catalog용 신규 REQ)을 명시 항목으로 포함. dcy-003 신규 REQ들(`GET /v1/portfolios` 등)과 dcy-001 batch의 의존성을 batch planning 단계에서 정리.
|
|
- R-4 (Medium): `/v1/portfolios*` 신규 라우트가 기존 `app/api/portfolios.py`의 legacy `/portfolios/*` 라우트와 책임 중복을 일으켜 API 사용자 혼동 및 코드 유지보수 비용 증가 가능.
|
|
- 완화 방안: 본 Discovery에서 두 라우트 set의 책임 경계와 client guidance(권장 path, deprecation 안내)를 `docs/interface-contract.md`에 명시한다. legacy 제거는 후속 Discovery로 분리하되 본 Discovery 시점에 deprecation 표시.
|
|
- R-5 (Low): `app/services/portfolio_service.py`의 메서드 시그니처에 catalog 검증을 추가하면 CLI(`pfhub portfolio create`) 경로에도 영향을 미친다. CLI 회귀 가능성.
|
|
- 완화 방안: catalog 검증을 service 계층 공통 진입점에 두되 CLI 경로의 기존 동작과 호환되도록 입력 형태(legacy: symbol+broker, 신규: asset_id)를 모두 처리하거나 CLI 측 입력 변환 단계를 추가한다. REQ 작성 시 CLI 영향 범위 명시.
|
|
|
|
## 가정
|
|
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
|
|
|
|
- A-1: dcy-001 FR-5는 dcy-001 draft-req 단계(2026-05-19)에서 REQ-013(`GET /v1/assets` 통합 endpoint)으로 작성되어 Approved 상태였으며, 본 Discovery 확정(2026-05-20) 후 REQ-013에 change-req(CHG-20260520-01) 적용으로 Deprecated 전환 완료. `GET /v1/assets` catalog 조회 의미는 REQ-010 보강 또는 신규 REQ로 dcy-001 batch design 단계에서 처리. 본 Discovery의 FR-9는 이 처리의 근거 문서다.
|
|
- A-2: dcy-002 portfolios.tag_ids 정책(`tags.is_admin_tag=TRUE` 또는 `tags.created_by_user_id = portfolio.owner_user_id`)은 본 Discovery FR-7의 portfolio tag 적용 라우트에서 그대로 적용된다. tag entity 자체의 CRUD는 dcy-002 범위.
|
|
- A-3: 기존 `app/services/portfolio_service.py`의 메서드는 catalog 검증을 추가하는 형태로 재사용 가능하다. 서비스 계층의 시그니처 변경은 catalog 검증 통합에 한정하며, 신규 메서드를 대량 추가하지 않는다.
|
|
- A-4: trex-basic은 portfolio-hub 본 batch release window 내에 클라이언트 코드를 갱신해 동시 배포 가능하다. 별도 별칭/redirect 운영 없이 deploy 시점 조율만으로 충분하다(OQ-3 Answered 2026-05-20). trex-basic 측 release readiness는 DATA_TREX_BASIC_RELEASE_WINDOW로 외부 확인이 남아 있다.
|
|
- A-5: 인증 모델은 현행 `app/core/auth.py`의 Permission 체계(ADMIN/TRADE_WRITE/READ) + `AuthContext`(token_source, token_type, user_id, daemon_id, runtime_id) 기반으로 충분하며, 본 Discovery는 신규 권한 체계나 토큰 종류를 도입하지 않는다.
|
|
|
|
## 오픈 질문
|
|
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
|
|
|
|
- OQ-1: `DELETE /v1/portfolios/{portfolio_id}` 방식 — soft delete(`status='disabled'` 전환, 데이터 보존) vs hard delete(DB row 제거, ledger 함께 또는 `?keep_ledger=true` 옵션 노출)?
|
|
- 상태: Answered
|
|
- 처리 방안: soft delete로 결정(2026-05-20). `status='disabled'` 전환 + `managed_by_daemon_id` NULL 자동 해제 + 의존 데이터(ledger·comments·tags) 보존. disabled 상태에서 write/도메인 동작은 모두 차단하고, 복구는 admin의 `PATCH status` 전환으로 수행. hard delete 경로는 v1에 노출하지 않는다.
|
|
- 반영 위치: FR-4 본문 갱신 완료, R-2 완화 방안 갱신 완료, §10 DECIDE에서 DECIDE_DELETE_STRATEGY 처리 완료.
|
|
- OQ-2: `POST /v1/portfolios`에서 daemon(service-token)이 portfolio를 직접 생성할 수 있는가? 허용 시 `owner_user_id`는 token에 연결된 owner_user_id로 강제되는가? 거부 시 HTTP 403 응답 정책은 어떻게 정의하는가?
|
|
- 상태: Answered
|
|
- 처리 방안: 거부로 결정(2026-05-20). daemon(service-token)은 `POST /v1/portfolios` 호출 시 token_type 검사 단계에서 HTTP 403 + `detail="service token cannot create portfolios"`로 fail-closed 응답한다. daemon은 v1 API에서 portfolio CRUD 주체가 아니며 권한은 조회(GET)와 ledger 기록(POST /ledger/trades)에 한정한다.
|
|
- 반영 위치: FR-1 본문 갱신 완료, 범위 경계 Out of Scope 갱신 완료, §10 DECIDE에서 DECIDE_DAEMON_CREATE_ALLOWANCE 처리 완료.
|
|
- OQ-3: trex-basic 마이그레이션 전략 — 한시적 별칭(구 `/v1/assets` portfolio 조회와 구 ledger path를 신 path로 redirect 또는 dual-serve) 없이 동시 배포만으로 충분한가, 아니면 별칭/소프트 deprecation 기간이 필요한가?
|
|
- 상태: Answered
|
|
- 처리 방안: 한시적 별칭/redirect 없이 동시 배포로 결정(2026-05-20). 구 path는 동일 release에서 제거되고 신 `/v1/portfolios*`만 단일 서빙. rollback 시 portfolio-hub 단독 rollback은 trex-basic을 깨뜨리므로 release plan에 trex-basic 동반 rollback 전제를 명시한다.
|
|
- 반영 위치: NFR-2 본문 갱신 완료, R-1 완화 방안 갱신 완료, A-4 갱신 완료, §10 DECIDE에서 DECIDE_TREX_MIGRATION_STRATEGY 처리 완료. trex-basic 측 release readiness(DATA_TREX_BASIC_RELEASE_WINDOW)는 외부 확인 항목으로 잔존.
|
|
- OQ-4: 매수/매도/입출금/이체 등 거래 동사를 별도 sub-route(`POST /v1/portfolios/{portfolio_id}/buy`, `/sell`, `/invest`, `/transfer`)로 노출할 것인가, `PATCH /v1/portfolios/{portfolio_id}`로 통합할 것인가, 또는 v1에는 `POST /v1/portfolios/{portfolio_id}/ledger/trades`만 노출하고 거래 동사는 CLI 전용으로 분리할 것인가?
|
|
- 상태: Answered
|
|
- 처리 방안: 옵션 C로 결정(2026-05-20). v1에는 `POST /v1/portfolios/{portfolio_id}/ledger/trades`만 노출하고 거래 동사(`buy`/`sell`/`invest`/`transfer`)는 본 Discovery 범위에서 CLI 전용으로 유지. 근거: (a) ledger 기록 경로 단일화 — daemon ledger/trades와 user 거래 동사 sub-route가 동일 portfolio에 ledger를 작성하는 책임 중복을 방지. (b) §3 user 시나리오에 거래 동사 HTTP 호출이 명시되지 않음 — 본 Discovery 핵심 가치는 CRUD + tags + comments + 조회. (c) v1 API 표면 최소화로 batch 인가/검증/테스트 부하 감소. (d) HTTP 노출이 후속에서 필요해지면 별도 Discovery에서 거래 의미론(사후 기록 vs broker 호출 위임) 정의 후 추가 — 회수 비용 낮음.
|
|
- 반영 위치: §5 범위 경계 Out of Scope 갱신 완료, §10 DECIDE에서 DECIDE_TRADE_VERB_ROUTING 처리 완료. FR 추가는 없음(기존 FR-6 ledger/trades 노출로 충분).
|
|
- OQ-5: 본 Discovery에서 일관 사용하는 "user token" 개념의 `AuthContext.token_type` 매핑은 무엇인가? 현행 코드의 token_type은 `session`(로그인 발급), `client`(CLI/API 발급), `service`(daemon-bound) 3종이며, env admin token과 DB admin token이 동일하게 admin 분기에 들어가는지도 함께 정의해야 한다. FR-1/FR-3/FR-5의 토큰 분기 정책은 이 매핑이 확정되어야 REQ에서 인가 헬퍼(`_require_portfolio_access` 등)로 구현 가능하다.
|
|
- 상태: Answered
|
|
- 처리 방안: token_type 게이트 + permission 기준 분기로 결정(2026-05-20). (a) `token_type ∈ {service, client}` → HTTP 403 fail-closed. service는 daemon-bound로 portfolio CRUD 주체가 아님(OQ-2). client는 발급 경로 자체를 [dcy-004](./dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md)(confirmed 2026-05-20)에서 폐지 확정 — 본 Discovery는 폐지 release 이전 운영에서도 v1 portfolio mutation 진입을 차단해 일관성 유지. (b) 그 외(`token_type=session` 또는 env token)는 `permission` 기준 분기 — admin이면 admin 분기, `read`/`trade-write`이면 user 분기. `token_source`(env/db)는 분기 기준에 사용하지 않으며 env admin token과 admin permission을 가진 session token은 admin 분기에서 동일 처리(dcy-004 OQ-5와 동일 결정). GET /v1/portfolios(FR-5)에서도 client 토큰은 거부되며 service 토큰은 daemon-bound 조회 의미로 별도 분기에서 정상 처리.
|
|
- 반영 위치: FR-1 본문 갱신 완료, FR-3 본문 갱신 완료, FR-4 본문 갱신 완료, FR-5 본문 갱신 완료, §3 사용자 시나리오 "session token"으로 명시 변경 완료, §10 DECIDE에서 DECIDE_USER_TOKEN_MAPPING 처리 완료. client token 발급 경로 폐지는 dcy-004(confirmed 2026-05-20)에서 확정되어 §10 DATA_CLIENT_TOKEN_DEPRECATION_DISCOVERY는 Collected 상태로 종료.
|
|
|
|
### 오픈 질문 처리 기준
|
|
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
|
|
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
|
|
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
|
|
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
|
|
- 처리 원칙 1: 질문만 남기지 말고 반드시 `상태`, `처리 방안`, `종료 조건`을 함께 적는다.
|
|
- 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
|
|
- 처리 원칙 3: High 영향 질문은 `Deferred` 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.
|
|
|
|
# 7. 초기 성공 기준
|
|
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
|
|
|
|
- S-1: `POST /v1/portfolios`를 admin/user 토큰으로 호출했을 때 토큰 타입별 ownership 규칙(admin + user_id body로 타 user 생성, user 본인 생성)이 정확히 적용되고, daemon(service-token) 호출은 HTTP 403으로 fail-closed 거부되며, dcy-001 catalog 검증(asset_id active hit/miss 케이스)이 기대대로 응답한다.
|
|
- S-2: `PATCH /v1/portfolios/{id}`와 `DELETE /v1/portfolios/{id}`에서 ownership 불일치 시 HTTP 403, 미인증 시 HTTP 401, 비존재 portfolio 호출 시 HTTP 404를 반환한다. daemon(service-token)이 PATCH/DELETE를 호출하면 token_type 검사 단계에서 HTTP 403 fail-closed로 거부된다(FR-1 CREATE 거부와 동일 패턴). DELETE 호출 후 `portfolios.status='disabled'` + `managed_by_daemon_id IS NULL` 사후 상태 및 의존 데이터(ledger/comments/tags) 보존이 검증 가능하다. disabled portfolio에 대한 write 차단(PATCH status 외 필드, ledger trade, tag/comment 변경)이 일관 적용된다.
|
|
- S-3: `GET /v1/portfolios`가 service token + daemon-scoped 필터, user token 본인 portfolio, admin token + `?user_id`로 타 user portfolio, `?market` 필터를 모두 정확히 처리한다. NULL daemon_id service token은 fail-closed(HTTP 403).
|
|
- S-4: `POST /v1/portfolios/{portfolio_id}/ledger/trades`가 구 `POST /v1/assets/{asset_id}/ledger/trades`와 동일한 idempotency, daemon assignment authorization 3단계, duplicate/conflict 응답(HTTP 200/409)을 보장한다. 동일 batch에서 구 path는 HTTP 404를 반환한다.
|
|
- S-5: `/v1/portfolios/{portfolio_id}/tags`와 `/v1/portfolios/{portfolio_id}/comments`가 ownership 규칙과 dcy-002 tag_ids 정책을 위반한 호출에 대해 HTTP 403을 반환한다.
|
|
- S-6: trex-basic이 본 batch release window에 새 path로 무중단 이전되어 release 후 portfolio-hub `/health`는 200 OK를 반환하고 sample ledger write가 정상 성공한다.
|
|
- S-7: baseline 문서(`docs/interface-contract.md`, `docs/runtime-flows.md`)에 `/v1/portfolios*` 명세, `/v1/assets*` 의미 회수, ledger path 이전 흐름이 모두 반영되어 후속 REQ/Batch가 해당 문서를 단일 source of truth로 사용할 수 있다.
|
|
|
|
## 측정 방식 및 데이터 출처
|
|
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
|
|
|
|
- S-1 측정 방식: pytest로 admin/user principal별 create 시나리오(catalog active hit/miss, ownership 정상/위반, admin `user_id` body 처리) + daemon principal 호출 시 HTTP 403 거부 회귀를 검증 / 데이터 출처: 신규 `tests/test_v1_portfolios_crud.py`
|
|
- S-2 측정 방식: pytest로 PATCH/DELETE의 ownership 위반·미인증·비존재 케이스 검증 + daemon(service-token) 호출 시 HTTP 403 fail-closed 거부 회귀 + DELETE 사후 상태(`status='disabled'`, `managed_by_daemon_id IS NULL`, 의존 데이터 보존) 확인 + disabled portfolio에 대한 후속 write(ledger/tag/comment) 차단 회귀 검증 / 데이터 출처: 신규 `tests/test_v1_portfolios_crud.py`
|
|
- S-3 측정 방식: pytest로 GET /v1/portfolios 토큰별 분기, `?user_id`, `?market` 필터, NULL daemon_id 403 회귀 검증 / 데이터 출처: 신규 `tests/test_v1_portfolios_list.py`
|
|
- S-4 측정 방식: 기존 `tests/test_v1.py`의 ledger trade idempotency·daemon authorization 케이스를 신 path로 갱신해 회귀 통과 + 구 path 호출 시 HTTP 404 확인 / 데이터 출처: 갱신 `tests/test_v1.py`, 신규 `tests/test_v1_portfolios_ledger.py`
|
|
- S-5 측정 방식: pytest로 tag/comment CRUD 시나리오와 dcy-002 정책(is_admin_tag, 소유자 검증) 위반 케이스 검증 / 데이터 출처: 신규 `tests/test_v1_portfolios_tags_comments.py`
|
|
- S-6 측정 방식: release verification checklist의 health endpoint + sample portfolio create → ledger write smoke / 데이터 출처: 본 Discovery 후속 release 문서의 verification 섹션
|
|
- S-7 측정 방식: baseline 문서 갱신 후 docs/interface-contract.md, docs/runtime-flows.md를 검토해 `/v1/portfolios*` 명세, `/v1/assets*` 의미 회수, ledger path 이전 항목이 모두 반영되었는지 확인 / 데이터 출처: 갱신된 baseline 문서
|
|
|
|
# 8. REQ로 넘기기 전 확인 체크
|
|
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
|
|
|
|
- [x] 범위(In Scope/Out of Scope) 확정
|
|
- 기준: OQ-1~5 전체 Answered 완료(2026-05-20). 토큰 분기 정책까지 명확해 In/Out of Scope 경계 최종화 완료. CONFIRM 2건 + DATA 2건은 §10에 잔존(사용자 확인 + 외부 데이터 + 후속 Discovery 작성 필요).
|
|
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
|
- 기준: S-1~7 측정 방식이 §7 측정 방식 섹션에 각각 신규/갱신 pytest 시나리오(`tests/test_v1_portfolios_crud.py`, `tests/test_v1_portfolios_list.py`, `tests/test_v1_portfolios_ledger.py`, `tests/test_v1_portfolios_tags_comments.py`) 또는 release verification 체크 항목(S-6/S-7)으로 명시 연결됨.
|
|
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
|
- 기준: R-1(trex-basic 동시 배포) 완화 방안 4단계(a~d) 명시, R-2(disabled 누적·write 차단) 완화 방안 명시, R-3 해소(REQ-013 Deprecated 처리 완료), R-4(legacy 라우트 책임 경계)·R-5(CLI 회귀) 완화 방안 명시. 책임자 = repository-maintainers(본 Discovery 단계). REQ 작성 시 작업별 책임자가 REQ Impacted Area에 반영될 예정.
|
|
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
|
- 기준: OQ-1~5 전체 Answered 완료(2026-05-20). Open 상태 OQ 0건.
|
|
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
|
- 기준: §11 Confirmed By에 Justin 지정 완료(2026-05-20).
|
|
- [x] dcy-001 FR-5 change-req 처리 계획 확정 + 실행
|
|
- 기준: REQ-013(Approved, dcy-001 FR-5 산출물)에 change-req CHG-20260520-01 적용 완료 (Status → Deprecated, 2026-05-20). dcy-001 §0 후속 Discovery 참조 갱신 + REQ-010 Notes 보강 처리 완료. 후속: `GET /v1/assets` catalog 명세는 dcy-001 batch design 단계에서 REQ-010 보강 또는 신규 REQ로 처리.
|
|
|
|
# 9. 파일 처리 결과
|
|
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
|
|
|
|
- 처리 결과: 생성
|
|
- 생성 경로: docs/discovery/dcy-003_20260520_user-portfolio-mutation-v1-api-migration.md
|
|
- 참조 문서:
|
|
- docs/discovery/dcy-001_20260519_asset-catalog-and-user-portfolio-model.md (선행 Discovery — FR-5 override 대상, Out of Scope 후속 예고의 본 Discovery 해당)
|
|
- docs/discovery/dcy-002_20260519_tag-ownership-and-applicability-policy.md (병행 Discovery — portfolio tag 적용 정책 준수)
|
|
- docs/discovery/dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md (후속 Discovery, confirmed 2026-05-20 — client token 발급 경로 폐지 + 토큰 surface 정비. 본 Discovery의 FR-1/3/4/5 token_type 게이트가 dcy-004 폐지 결정과 정합)
|
|
- temp/20260519_old_sdlc/discovery/dcy-007_20260518_v1-asset-crud-endpoints.md (참조 — 폐기된 옛 SDLC의 draft 초안, FR/OQ 일부 승계 및 재구성)
|
|
- docs/interface-contract.md (현행 v1 API 계약, 본 Discovery로 갱신 예정)
|
|
- docs/runtime-flows.md (현행 v1 API flow, 본 Discovery로 갱신 예정)
|
|
- docs/project-structure.md (현행 패키지/모듈 baseline, v1 라우터 파일 추가에 따라 일부 영향)
|
|
|
|
# 10. 사용자 결정 필요 항목 요약
|
|
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
|
|
|
|
## DECIDE
|
|
- ~~{{DECIDE_DELETE_STRATEGY}}~~: **Decided** — soft delete(`status='disabled'` 전환, 데이터 보존)로 확정. hard delete 경로는 v1에 노출하지 않음. FR-4·R-2·OQ-1 본문 반영 완료. (OQ-1 Answered 2026-05-20)
|
|
- ~~{{DECIDE_DAEMON_CREATE_ALLOWANCE}}~~: **Decided** — daemon(service-token)의 `POST /v1/portfolios` 호출 거부 확정. HTTP 403 + `detail="service token cannot create portfolios"` fail-closed 응답. FR-1·범위 경계 Out of Scope·OQ-2 본문 반영 완료. (OQ-2 Answered 2026-05-20)
|
|
- ~~{{DECIDE_TREX_MIGRATION_STRATEGY}}~~: **Decided** — 한시적 별칭/redirect 없이 trex-basic + portfolio-hub 동시 배포로 확정. 구 path는 동일 release에서 제거. rollback 시 trex-basic 동반 rollback 전제. NFR-2·R-1·A-4·OQ-3 본문 반영 완료. (OQ-3 Answered 2026-05-20)
|
|
- ~~{{DECIDE_TRADE_VERB_ROUTING}}~~: **Decided** — 옵션 C로 확정. v1에는 `POST /v1/portfolios/{portfolio_id}/ledger/trades`만 노출, 거래 동사(buy/sell/invest/transfer)는 본 Discovery 범위에서 CLI 전용 유지. ledger 기록 경로 단일화, v1 표면 최소화, 후속 Discovery에서 거래 의미론 정의 후 추가 가능. §5 범위 경계 Out of Scope·OQ-4 본문 반영 완료. (OQ-4 Answered 2026-05-20)
|
|
- ~~{{DECIDE_USER_TOKEN_MAPPING}}~~: **Decided** — token_type 게이트 + permission 기준 분기. `service`/`client` token은 v1 portfolio mutation 진입 차단(HTTP 403). `session` 또는 env token은 `permission`이 admin이면 admin 분기, 그 외는 user 분기. `token_source`(env/db)는 분기 기준에 사용하지 않음. FR-1·FR-3·FR-4·FR-5·§3 시나리오·OQ-5 본문 반영 완료. client token 발급 경로 폐지는 후속 Discovery로 분리(DATA 항목 추적). (OQ-5 Answered 2026-05-20)
|
|
|
|
## CONFIRM
|
|
- ~~{{CONFIRM_DCY001_FR5_CHANGE_REQ}}~~: **Confirmed + Executed (2026-05-20)** — 옵션 B-3(REQ-013 Deprecated + REQ-010 보강 + 신규 REQ로 분리) 채택. REQ-013(Approved, dcy-001 FR-5 산출물)에 change-req CHG-20260520-01 적용 완료, Status → Deprecated. dcy-001 §0 후속 Discovery 참조 갱신, REQ-010 Notes 보강, docs/srs/index.md Register/Change Log Summary 갱신 완료. `GET /v1/assets` catalog 명세 처리는 dcy-001 batch design 단계로 이관.
|
|
- ~~{{CONFIRM_LEGACY_PORTFOLIOS_ROUTE_SCOPE}}~~: **Confirmed (2026-05-20)** — 본 Discovery는 `/v1/portfolios*` 신설만 다루고 기존 `app/api/portfolios.py`의 legacy `/portfolios/*` 라우트 제거는 후속 Discovery로 분리하는 범위 결정에 사용자 동의 완료. §4 영향 범위·§5 Out of Scope·R-4 본문에 이미 명시됨. 후속 Discovery 작성 시점은 본 Discovery REQ drafting 또는 batch 진행 중 별도 결정.
|
|
|
|
## DATA
|
|
- ~~{{DATA_TREX_BASIC_RELEASE_WINDOW}}~~: **External tracking (release plan 단계 수집)** — trex-basic 배포 운영 담당자가 본 batch release window에 동시 배포 가능한 일정과 rollback 절차를 확정해 제공한다. 본 Discovery는 정책(NFR-2 동시 배포·R-1 완화 방안)을 정의하고 실제 release window 확정은 batch design / release plan 단계 운영 작업이라 confirm-discovery 게이트 차단 사유 아님. release plan 작성 시 evidence(예: trex-basic 배포 일정, rollback 절차 협의 결과)를 batch verification 문서에 기록.
|
|
- ~~{{DATA_CLIENT_TOKEN_DEPRECATION_DISCOVERY}}~~: **Collected (2026-05-20)** — 후속 Discovery [dcy-004](./dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md) (confirmed 2026-05-20) 작성 완료. dcy-004 FR-1에서 `POST /auth/tokens`, `issue_client_token`, `CreateClientTokenRequest`, `IssuedClientTokenResponse` 모두 폐지로 확정. DATA-1 inventory 0건 확인으로 grace period/revoke 스크립트 불필요. 본 Discovery의 FR-1/3/4/5 token_type 게이트는 dcy-004 폐지 release 이전·이후 모두 정합.
|
|
|
|
# 11. Discovery Freeze
|
|
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
|
|
|
|
## 섹션 참조
|
|
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
|
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
|
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
|
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
|
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
|
|
|
## Freeze 플래그
|
|
- Handoff Decision: 승인
|
|
- Handoff Rationale: OQ-1~5 Answered + CONFIRM 2건 Confirmed + DATA-1 Collected(dcy-004) + DATA-2 External tracking(release plan 단계) + §8 체크리스트 6건 모두 [x] 완료(2026-05-20). confirm-discovery 게이트 통과로 본 Discovery는 `confirmed` 상태로 승급되어 REQ 초안 작성 가능. **외부 의존**: DATA_TREX_BASIC_RELEASE_WINDOW(release plan 단계 수집), 향후 batch design 단계에서 dcy-001 REQ-010 보강 또는 신규 REQ(`GET /v1/assets` catalog 명세) 처리.
|
|
- Ready for REQ Drafting: true
|
|
- Confirmed By: Justin
|
|
- Confirmed At (KST): 2026-05-20T14:00+09:00
|