# BAT-006 Implementation - Status: completed - Batch: BAT-006 ## Plan Summary - REQ-016: 신규 Alembic revision(name 컬럼 추가, portfolio_id UUID 교체, FK 4개 테이블 atomic 갱신, UNIQUE 제약 추가), Portfolio dataclass `name` 필드 추가, `create_portfolio()` UUID 생성, `get_portfolio_by_name_and_user()` 신규 메서드 - REQ-017: CLI portfolio 명령 argument/option 전면 전환(`portfolio_id` → ` --user`), `(user_id, name)` → UUID 서비스 레이어, name 유효성 검사, update name guard - REQ-018: HTTP API path parameter UUID 전환, `GET /v1/assets` `asset_id` UUID 직렬화, dcy-002 A-3 철회 주석 - REQ-019: baseline 문서 4개 갱신(project-structure.md, runtime-flows.md, data-model.md, interface-contract.md) ## Changed Files ### New Files - `migrations/versions/20260511_0015_portfolio_uuid_migration.py` — Alembic revision; idempotency guard via uq_portfolios_owner_name constraint presence check; atomic per-portfolio FK update across portfolio_ledger/portfolio_comments/portfolio_tags/ledger_trades - `tests/test_portfolio_uuid.py` — 14 tests covering UUID creation, name+user resolution, alice/bob isolation, buy/delete flow, name validation, unknown user/portfolio errors, update rename ### Modified Files - `app/repositories/postgres.py` - `ensure_schema()`: replaced `ADD CONSTRAINT IF NOT EXISTS` (not valid PostgreSQL syntax) with `DO $$ IF NOT EXISTS ... ALTER TABLE ADD CONSTRAINT $$` pattern for uq_portfolios_owner_name - `create_portfolio()`: signature changed from `(portfolio_id: str, *, ..., name: str, ...)` to `(name: str, *, ...)` with server-generated UUID; catches psycopg UniqueViolation → ValueError("이미 사용 중인 이름입니다: {name}") - Added `get_portfolio_by_name_and_user(owner_user_id, name) -> Portfolio | None` - `update_portfolio()`: catches UniqueViolation on name update → ValueError("이미 사용 중인 이름입니다: {new_name}") - `app/domain/services.py` — `PortfolioDomainRepository` Protocol updated: `create_portfolio` new signature, `get_portfolio_by_name_and_user` added, `list_portfolios` Protocol params aligned - `app/schemas/portfolio.py` — `CreatePortfolioRequest`: removed `portfolio_id` field; `name: str` now required with regex validation `[a-zA-Z0-9._:-]{1,100}` - `app/services/portfolio_service.py` — `create_portfolio()`: call site updated to `self._repository.create_portfolio(payload.name, owner_user_id=...)` - `app/cli/commands/portfolio.py` — Full rewrite for REQ-017: `_NAME_RE`, `_validate_portfolio_name()`, `_resolve_portfolio_id()` helpers; all 8 mutation commands switched to ` --user`; `update` uses `--new-name` with `--force` guard; `comment`/`tag` sub-commands keep `portfolio_id` (UUID) directly - `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md` — A-3 철회 주석 추가 - `docs/data-model.md` — portfolios.portfolio_id → UUID, name column, uq_portfolios_owner_name constraint documented - `docs/project-structure.md` — Added `get_portfolio_by_name_and_user()` to repositories section - `docs/runtime-flows.md` — Added CLI portfolio name+user resolution flow; GET /v1/assets asset_id UUID note - `docs/interface-contract.md` — Added v1 consumer API section with simultaneous deployment constraint; CLI portfolio inputs updated - `tests/test_cli.py` — FakeRepo: `create_portfolio` new signature (generates UUID), `get_portfolio_by_name_and_user` added; `_portfolio()`: name changed to "005930:tv"; 5 tests updated to pass --user and adjust assertions - `tests/test_portfolio_ownership.py` — Fixed POST body to use `name` instead of `portfolio_id` ## Execution Log 1. Created Alembic migration `20260511_0015_portfolio_uuid_migration.py` (REQ-016) 2. Updated `app/repositories/postgres.py`: ensure_schema UNIQUE constraint (DO $$ pattern), create_portfolio UUID generation, get_portfolio_by_name_and_user, update_portfolio UniqueViolation catch (REQ-016) 3. Updated `app/domain/services.py` Protocol (REQ-016) 4. Updated `app/schemas/portfolio.py`: removed portfolio_id, name required + validated (REQ-016, REQ-017) 5. Updated `app/services/portfolio_service.py`: create_portfolio call site (REQ-016) 6. Rewrote `app/cli/commands/portfolio.py`: name+user pattern for all mutation commands (REQ-017) 7. Added dcy-002 A-3 철회 주석 (REQ-018) 8. Updated 4 baseline docs (REQ-019) 9. Created `tests/test_portfolio_uuid.py` with 14 tests 10. Fixed `tests/test_cli.py` FakeRepo + 5 test cases 11. Fixed `tests/test_portfolio_ownership.py` POST body field 12. Fixed `ensure_schema()` syntax: `ADD CONSTRAINT IF NOT EXISTS` → DO $$ block (PostgreSQL compatibility) ## Validation - Full test suite: **129 passed, 0 failed** - `tests/test_portfolio_uuid.py`: 14/14 passed (UUID generation, name+user resolution, alice/bob isolation, name validation, error messages) - `tests/test_cli.py`: 49/49 passed (including updated tests for new CLI interface) - `tests/test_portfolio_ownership.py`: 4/4 passed ## Remaining Risks - Alembic migration `20260511_0015` not verified against real PostgreSQL instance (unit tests use FakeRepo only) - trex-basic simultaneous deployment required: GET /v1/assets asset_id changes from TEXT to UUID format - Existing `comment` and `tag` sub-commands still take `portfolio_id` (UUID) directly; operators must obtain UUID from `portfolio list --user` output