# BAT-010 Planning - Status: confirmed - Batch: BAT-010 - Profile: standard ## Included REQ - REQ-030: portfolio tag 적용/해제/조회 v1 라우트 (Interface, Medium) - REQ-031: portfolio comment CRUD v1 라우트 (Interface, Medium) - REQ-032: portfolio v1 baseline 문서 갱신 (Documentation, Medium) ## Delivery Plan ### 구현 범위 및 순서 1. **REQ-030 기존 라우트 보강 + GET 신설** - 기존 `POST /v1/portfolios/{id}/tags`, `DELETE /v1/portfolios/{id}/tags/{tag_id}` 라우트 감사 결과: - ownership 검증 ✓, tag 정책 검증 ✓ (POST만) - disabled portfolio 보호 **누락** → AC-8 충족을 위해 추가 필요 - `GET /v1/portfolios/{id}/tags` 신설: `portfolio.tag_ids` → `repo.get_tags_by_ids()` → `PortfolioTagListResponse` - schema 추가: `PortfolioTagListResponse(tags: list[TagItem], total: int)` 2. **REQ-031 comment CRUD 구현** - 신규 4개 라우트: `POST`, `GET`, `PATCH`, `DELETE /v1/portfolios/{id}/comments[/{comment_id}]` - 기존 repo 메서드 재사용: `create_comment`, `list_comments`, `update_comment`, `delete_comment` - schema 추가 (app/schemas/v1.py): `PortfolioCommentItem`, `PortfolioCommentRequest`, `PortfolioCommentUpdateRequest`, `PortfolioCommentListResponse` - ownership 검증: `_get_portfolio_or_404` 헬퍼 재사용 (BAT-009 패턴) - disabled 보호: POST/PATCH/DELETE → HTTP 409; GET → 허용 - comment 작성자 ownership: portfolio ownership만 적용 (REQ-031 Notes 결정) 3. **테스트 작성** - `tests/test_v1_portfolios_tags_comments.py` 신규 - REQ-030 AC-1~11, REQ-031 AC-1~11 커버 - BAT-009의 `_with_repo` 패치 패턴 재사용 4. **REQ-032 baseline 문서 갱신** - `docs/interface-contract.md`: v1 portfolio CRUD 엔드포인트 섹션 추가 (REQ-025~028), GET /v1/portfolios/{id}/tags 엔드포인트 추가, comment CRUD 엔드포인트 추가; POST /v1/assets/{asset_id}/ledger/trades 섹션 "pending REQ-029" 마킹 - `docs/runtime-flows.md`: portfolio mutation flow + GET /v1/portfolios 토큰 분기 + soft delete flow 추가 - `docs/data-model.md`: Portfolio.status='disabled' 의미 + 의존 데이터 보존 정책 추가 - `docs/project-structure.md`: app/api/v1_portfolios.py 책임 갱신 + app/schemas/v1.py schema 목록 갱신 ### 선행 조건 - BAT-009 Implemented (REQ-025~028) ✓ — `_get_portfolio_or_404`, `_require_write_token`, `_close_repo` 헬퍼 재사용 - BAT-003 release-candidate (REQ-001/003): `portfolios.tag_ids UUID[]`, `repo.append_portfolio_tag_id/remove_portfolio_tag_id/get_tags_by_ids` ✓ - BAT-004 release-candidate (REQ-004/005): POST/DELETE /v1/portfolios/{id}/tags 이미 구현 ✓ - `app/repositories/portfolio_repository.py`: `create_comment`, `list_comments`, `update_comment`, `delete_comment` 추상 메서드 이미 정의 ✓ - `app/domain/models.py`: `PortfolioComment(id, portfolio_id, content, created_at, updated_at)` ✓ ## Dependencies - Internal: - BAT-009(REQ-025~028): `_get_portfolio_or_404`, `_require_write_token` 헬퍼, `PortfolioItem` 스키마 패턴 - BAT-003(REQ-001~003): `portfolios.tag_ids`, `repo.get_tags_by_ids` - BAT-004(REQ-005): POST/DELETE tag 라우트 (보강 대상) - External: - 없음 ## Design Gate - Design Required Before Implementation: yes - Reason: - REQ-031 comment 작성자 ownership 정책 확정: portfolio ownership만 적용 (결정 완료) - REQ-030 GET 응답 schema 확정: `PortfolioTagListResponse(tags: list[TagItem], total: int)` (결정 완료) - REQ-032 ledger path 마킹 방식 확정: "pending REQ-029" 주석 처리 (결정 완료) - 모든 설계 gate가 사전 해소됨 → design.md 확정 후 구현 착수 가능 ## Milestones 1. planning.md confirmed ✓ (현재 단계) 2. design.md confirmed — key decisions 기록 3. REQ-030 disabled 보호 보강 + GET 신설 + tests 4. REQ-031 comment CRUD 4종 + tests 5. REQ-032 baseline docs 4종 갱신 6. 전체 테스트 스위트 통과 7. verification.md 완성 ## Risks - R-1 (Low): 기존 POST/DELETE tag 라우트에 disabled 보호 추가 시 `test_tags_http_api.py` 회귀 가능 → 기존 테스트가 disabled portfolio를 사용하지 않으면 영향 없음; 확인 후 보강 - R-2 (Low): `repo.get_tags_by_ids(portfolio.tag_ids)` — tag_ids가 빈 배열일 경우 빈 dict 반환 → 빈 list 응답으로 정상 처리 - R-3 (Low): `update_comment`/`delete_comment` postgres 구현에서 `portfolio_comments` 테이블 존재 여부 확인 필요