Files
portfolio-hub/docs/srs/Interface/req-027_v1-portfolios-soft-delete.md
T
genichinandClaude Sonnet 4.6 c815ce39b6 feat: dcy-003/004 토큰 surface 개편 및 daemon lifecycle 정책 구현 (BAT-006/007)
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>
2026-05-21 01:03:39 +00:00

6.2 KiB

REQ-027: DELETE /v1/portfolios/{portfolio_id} 신설 (soft delete)

  • Status: Approved
  • Type: Interface
  • Priority: High
  • Owner: Justin Ko

Intent

  • v1 API에 portfolio 종료 경로(DELETE /v1/portfolios/{portfolio_id})를 신설해 user/admin이 HTTP로 portfolio를 종료할 수 있도록 한다.
  • dcy-003 OQ-1 결정에 따라 soft delete(status='disabled')로 구현해 데이터 보존성과 운영 복구 가능성을 확보한다. hard delete 경로는 v1에 노출하지 않는다.

Requirement

  • 신규 라우트 DELETE /v1/portfolios/{portfolio_id} (Bearer 인증 필수).
  • 토큰 분기 규칙 (REQ-025/026과 동일 패턴):
    • token_type ∈ {service, client} → HTTP 403 fail-closed.
    • 그 외(token_type=session 또는 env token):
      • permission='admin' → 모든 portfolio 삭제 가능.
      • permission ∈ {read, trade-write} → 자기 소유(owner_user_id = auth.user_id) portfolio만, 불일치 시 HTTP 403.
  • soft delete 동작:
    • portfolios.status = 'disabled'로 전환
    • managed_by_daemon_id는 NULL로 자동 해제
    • updated_at 갱신
    • 의존 데이터(portfolio_ledger, ledger_trades, portfolio_comments, portfolios.tag_ids) 모두 보존
  • disabled 상태 write 차단 (REQ-026/029/030/031에서도 검증되는 정책):
    • PATCH(status 전이 외 필드 변경) → HTTP 409 또는 422
    • POST /ledger/trades → 차단(REQ-029에서 처리)
    • tag/comment 추가·수정·삭제 → 차단(REQ-030/031에서 처리)
  • 복구 경로: admin이 PATCH /v1/portfolios/{portfolio_id} body {"status": "enabled"}(또는 이전 status)로 전환(REQ-026 AC-9). daemon 재배정은 별도 호출.
  • 비존재 portfolio_id는 HTTP 404. ownership 불일치는 HTTP 403.
  • 이미 disabled 상태인 portfolio에 재호출 시 HTTP 200 + 멱등 응답(또는 HTTP 409 — 본 REQ 구현 시 확정).
  • 응답: HTTP 200 또는 204 + (HTTP 200 시) disabled 상태 portfolio 또는 확정 응답.

Acceptance Criteria

  1. Given session token + 본인 active portfolio, When DELETE /v1/portfolios/{id}, Then HTTP 200/204 + portfolios.status='disabled' + managed_by_daemon_id IS NULL 사후 상태.
  2. Given AC-1 실행 후, When portfolio_ledger/ledger_trades/portfolio_comments/portfolios.tag_ids 조회, Then 의존 데이터가 모두 보존되어 있다.
  3. Given admin token + 타 user active portfolio, When DELETE /v1/portfolios/{id}, Then HTTP 200/204 + 동일 사후 상태.
  4. Given session token + 타 user portfolio, When DELETE /v1/portfolios/{id}, Then HTTP 403 (ownership 불일치).
  5. Given service token, When DELETE /v1/portfolios/{id}, Then HTTP 403 fail-closed.
  6. Given client token(폐지 전), When DELETE /v1/portfolios/{id}, Then HTTP 403 fail-closed.
  7. Given Bearer token 없음, When DELETE /v1/portfolios/{id}, Then HTTP 401.
  8. Given 비존재 portfolio_id, When DELETE /v1/portfolios/{id}, Then HTTP 404.
  9. Given disabled portfolio + admin PATCH status='enabled', When PATCH, Then HTTP 200 + 복구. (REQ-026 AC-9와 연계)
  10. Given AC-1 실행 후 disabled portfolio에 ledger trade/tag/comment write 시도, When 시도, Then 모두 차단(HTTP 409 또는 422). (REQ-029/030/031과 연계 검증)

Impacted Area

  • Docs:
    • docs/interface-contract.md — DELETE /v1/portfolios/{id} 명세 (REQ-032)
    • docs/runtime-flows.md — soft delete flow + disabled write 차단 + 복구 (REQ-032)
    • docs/data-model.md — Portfolio.status='disabled' 의미 + 의존 데이터 보존 정책 (REQ-032)
  • Modules:
    • app/api/v1_portfolios.py — DELETE 라우트
    • app/services/portfolio_service.py:delete — 기존 메서드 재사용 시 soft delete로 변경 또는 신규 soft_delete_portfolio 헬퍼
    • app/repositories/portfolio_repository.py, app/repositories/postgres.py — set_portfolio_status(portfolio_id, status, clear_daemon=True) 메서드 신규 또는 기존 update_portfolio 활용
  • Tests:
    • 신규/갱신 tests/test_v1_portfolios_crud.py — AC-1~10 검증

Notes

  • dcy-003 R-2(disabled 누적 가시성·write 차단 일관성)는 본 REQ + REQ-028 ?include_disabled opt-in 정책 + REQ-026/029/030/031의 차단 검증으로 함께 완화.
  • 누적 disabled 정리(아카이브, 영구 삭제 운영 SOP)는 본 Discovery 범위 밖. 별도 운영 Discovery로 위임.
  • 본 REQ는 hard delete 경로를 v1에 노출하지 않으므로 hard 삭제 운영은 DB 직접 접근 또는 별도 운영 도구로만 가능.
  • Source: dcy-003 FR-4, OQ-1, OQ-2, OQ-5, NFR-1, NFR-3, NFR-4.

Change Log

CHG-20260520-02

  • Date: 2026-05-20
  • Author: Justin Ko

Change Summary

  • Status: Proposed → Approved

Intent

  • 승인 게이트 통과: 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 모두 충족, AC 구체성 확인 완료, blocker 없음

Acceptance Criteria Delta

  • No change

Impacted Area Delta

  • No change

Delivery Trace Delta

  • Batches: none (미정)
  • Releases: none (미정)

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: promote-to-approved
  • Follow-up Action: batch 계획 편입

CHG-20260520-01

  • Date: 2026-05-20
  • Author: Justin Ko

Change Summary

  • 최초 생성 — dcy-003 confirm-discovery 후 draft-req 실행. dcy-003 FR-4 산출물.

Intent

  • v1 portfolio 종료 경로 + soft delete 정책 명시.

Acceptance Criteria Delta

  • Added:
    • AC-1~10 최초 작성

Impacted Area Delta

  • Docs: docs/interface-contract.md, docs/runtime-flows.md, docs/data-model.md (REQ-032)
  • Modules: app/api/v1_portfolios.py, app/services/portfolio_service.py, app/repositories/* — 최초 지정
  • Tests: tests/test_v1_portfolios_crud.py — 최초 지정

Delivery Trace Delta

  • Batches: none (미정)
  • Releases: none (미정)

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: keep-current
  • Follow-up Action: confirm-req 후 batch 계획 편입; REQ-025/026과 동일 batch 권장

Validation Plan

  • pytest TestClient로 AC-1~10 검증; soft delete 사후 상태 + 의존 데이터 보존 + 복구 경로 + write 차단 일관성