# req-017: CLI portfolio 명령 — name+user 조합 식별 방식 전환 - Status: Implemented - Type: Interface - Priority: High - Owner: repository-maintainers ## Intent - 현재 `portfolio_id` Argument로만 portfolio를 식별하는 CLI 뮤테이션 명령을 `` + `--user ` 조합으로 전환해 소유권 검증을 명령 실행 경로에 포함한다. - CLI 내부에서 `(owner_user_id, name)` → UUID 단일 쿼리 조회 후 처리하고, 조회 실패 시 명확한 오류 메시지를 출력한다. ## Requirement - `portfolio create` 명령은 `name` Argument와 `--user ` 옵션을 필수로 받는다. `portfolio_id`는 서버가 UUID로 발급하며 사용자가 직접 지정하거나 조회할 수 없다. - `status`, `buy`, `sell`, `invest`, `transfer`, `reset`, `delete`, `update` 명령은 `` Argument와 `--user ` 옵션으로 portfolio를 식별한다. 내부에서 `(owner_user_id, name)` → `portfolio_id(UUID)`를 단일 DB 쿼리로 조회한 뒤 처리한다. - `` + `--user` 조합으로 portfolio를 찾지 못하면 오류 메시지 `포트폴리오를 찾을 수 없습니다: {name} (user: {username})`를 출력하고 명령을 중단한다. - `name`은 `update` 명령으로 변경 가능하다. 변경 시 동일 `owner_user_id` 내에 같은 `name`이 이미 존재하면 오류 메시지 `이미 사용 중인 이름입니다: {name}`를 반환한다. - `name` 변경 명령에는 명시적 확인 절차(`--force` 또는 confirm 프롬프트)를 요구한다. - `name` 허용 문자는 `[a-zA-Z0-9._:-]`이며, 공백 불허, 1~100자, 대소문자 구분이 있다. 이 범위를 벗어나면 유효성 검사 오류를 반환한다. ## Acceptance Criteria 1. Given `pfhub portfolio create --user ...`을 실행하면, When 서버가 UUID를 발급하면, Then CLI가 portfolio_id를 직접 지정하거나 조회하지 않고 UUID가 DB에 저장된다. 2. Given `pfhub portfolio buy --user alice ...`를 실행하면, When `(alice, )` 조합으로 DB를 단일 쿼리로 조회해 UUID를 찾으면, Then 해당 portfolio에 buy 작업이 수행된다. 3. Given `--user bob`으로 alice 소유의 portfolio ``을 조작하면, When `(bob, )` 조합으로 조회해 레코드가 없으면, Then `포트폴리오를 찾을 수 없습니다: {name} (user: bob)` 오류가 출력되고 명령이 중단된다. 4. Given alice와 bob이 동일 name `tv`로 각각 portfolio를 생성하면, Then 두 레코드가 독립적으로 저장되고 각자 자신의 `tv` portfolio를 조작할 수 있다. 5. Given alice의 portfolio name을 `tv2`로 변경할 때 alice에게 이미 `tv2` 이름의 portfolio가 존재하면, When `update` 명령이 DB의 `(owner_user_id, name)` UNIQUE 제약 위반을 감지하면, Then `이미 사용 중인 이름입니다: tv2` 오류를 출력한다. 6. Given `--force` 없이 `update` 명령으로 name을 변경하려 하면, Then `--force`가 없으면 변경이 중단되거나 confirm 프롬프트가 표시된다. 7. Given `name` 허용 문자 범위(`[a-zA-Z0-9._:-]`) 밖의 문자가 포함된 이름을 `create` 시 입력하면, Then 유효성 검사 오류가 반환된다. 8. Given `name` 길이가 0이거나 100자를 초과하면, Then 유효성 검사 오류가 반환된다. ## Impacted Area - Docs: `docs/runtime-flows.md` (CLI 명령 흐름 변경 반영 — REQ-019에서 처리) - Modules: - `app/cli/commands/portfolio.py`: `create`, `status`, `buy`, `sell`, `invest`, `transfer`, `reset`, `delete`, `update` 명령의 Argument/Option 전환 - `app/domain/services.py`: `(user_id, name)` → UUID 해석 로직 추가 - `app/repositories/postgres.py`: `get_portfolio_by_name_and_user()` 단일 쿼리 구현 (REQ-016과 공유) - Tests: - `tests/test_cli.py`: 소유권 검증 케이스, 오류 메시지 검증, name 유효성 검사, name 중복 변경 거부 테스트 ## Notes - `(owner_user_id, name)` → UUID 조회는 REQ-016에서 추가하는 `get_portfolio_by_name_and_user()` 메서드를 사용한다. - `name` 대소문자 구분 있음 — `TV`와 `tv`는 다른 이름으로 처리. - Source Discovery: dcy-004_20260511_portfolio-id-uuid-migration-and-user-scoped-name-resolution (FR-2, FR-3, FR-3a, NFR-2) ## Change Log ### CHG-20260511-01 - Date: 2026-05-11 - Author: repository-maintainers #### Change Summary - 최초 생성 — dcy-004 draft-req에 의해 생성 #### Intent - dcy-004 Discovery의 FR-2(portfolio create 명령 name+user 전환), FR-3(CLI 뮤테이션 명령 식별 방식 전환), FR-3a(name mutable + 중복 변경 거부), NFR-2(단일 DB 쿼리, 오류 메시지) 반영 #### Acceptance Criteria Delta - Added: - 1~8 (최초 생성) #### Impacted Area Delta - Docs: - docs/runtime-flows.md: CLI 명령 흐름 변경 (REQ-019에서 처리) - Modules: - app/cli/commands/portfolio.py: 명령 Argument/Option 전면 교체 - app/domain/services.py: (user_id, name) → UUID 해석 추가 - app/repositories/postgres.py: get_portfolio_by_name_and_user() 추가 - Tests: - tests/test_cli.py: 소유권 검증, 오류 메시지, name 유효성 검사 케이스 추가 #### Delivery Trace Delta - Batches: none (batch 미결정) - Releases: none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: confirm-req → suggest-batch-reqs → draft-batch #### Validation Plan - pytest: 소유권 검증 케이스, 오류 메시지 검증, name 유효성 검사, name 중복 변경 거부, alice/bob 독립 portfolio 생성 검증 ### CHG-20260511-02 - Date: 2026-05-11 - Author: repository-maintainers #### Change Summary - Approved 전환 — confirm-req 게이트 통과 #### Intent - Delivery 입력으로 사용할 수 있도록 REQ-017을 승인 상태로 승격한다. #### Acceptance Criteria Delta - Added: - none - Updated: - none - Removed: - none #### Impacted Area Delta - Docs: - none - Modules: - none - Tests: - none #### Delivery Trace Delta - Batches: - none - Releases: - none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: suggest-batch-reqs로 batch grouping 검토 #### Validation Plan - confirm-req 게이트 재평가 결과: 필수 메타데이터, 구체적 Acceptance Criteria, Impacted Area, Change Log, blocker 없음 확인 ### CHG-20260511-03 - Date: 2026-05-11 - Author: repository-maintainers #### Change Summary - Implemented 전환 — BAT-006 verification passed, confirm-req-implemented 게이트 통과 #### Intent - BAT-006 verification.md의 REQ-017 AC-1~8 모두 test evidence 포함. Blocking Issues 없음. 구현 완료 상태로 전환한다. #### Acceptance Criteria Delta - none #### Impacted Area Delta - none #### Delivery Trace Delta - Batches: BAT-006 (release-candidate) - Releases: none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: implemented - Follow-up Action: draft-release (BAT-006) #### Validation Plan - BAT-006 verification.md AC-1~8 모두 test evidence 연결 확인 (tests/test_portfolio_uuid.py, tests/test_cli.py 전체 통과)