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>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
# REQ-019: 토큰 surface baseline 문서 정합성 회복
|
||||
|
||||
- Status: Approved
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 현행 baseline 문서가 토큰 surface 결함(rebind 명세 누락, AuthContext daemon_id 필드 누락) + 본 batch 변경 사항(client token 폐지, rotate index 보강, HTTP daemon_id forward, auth_tokens 인덱스 변경)을 모두 반영해 후속 REQ·batch가 단일 source of truth로 사용할 수 있도록 정합성을 회복한다.
|
||||
- dcy-004 FR-4와 FR-10을 단일 REQ로 통합 처리한다(둘 다 baseline 갱신 작업).
|
||||
|
||||
## Requirement
|
||||
- `docs/interface-contract.md` 갱신:
|
||||
- `pfhub service-token rebind` CLI 명세 추가([app/cli/commands/service_tokens.py:119-141](app/cli/commands/service_tokens.py#L119-L141) 구현 반영)
|
||||
- `POST /auth/tokens`(client token 발급) 명세 제거(REQ-016 처리 반영)
|
||||
- `POST /auth/service-tokens` body `daemon_id` forward 동작 명시(REQ-018 처리 반영)
|
||||
- `docs/runtime-flows.md` 갱신:
|
||||
- `AuthContext` 필드 목록에 `daemon_id` 추가([docs/runtime-flows.md:76](docs/runtime-flows.md#L76) 보강)
|
||||
- 토큰 surface 3종(session/service/env)으로 축소 반영
|
||||
- rotate runtime_id 갱신 + daemon_id invariant 보존 흐름 명시(REQ-017 처리 반영)
|
||||
- `docs/data-model.md` 갱신:
|
||||
- `auth_tokens` 인덱스 변경(`idx_auth_tokens_service_name_unique`에 `AND revoked_at IS NULL` 조건 추가) 반영
|
||||
- 토큰 타입 surface 정리(client 제거)
|
||||
- `docs/project-structure.md` 갱신:
|
||||
- 신규 마이그레이션 스크립트 또는 CLI 서브커맨드(REQ-022 자동 revoke, REQ-023 NULL daemon 마이그레이션) 추가에 따른 패키지 책임 갱신 — 필요 시
|
||||
- 갱신은 baseline 정합성 회복에 한정한다. 새 정책 정의(매트릭스, TTL 정책, SOP)는 REQ-020/021/024가 별도로 다룬다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `grep -n "service-token rebind" docs/interface-contract.md`, When 본 REQ 구현 후, Then 명세 entry가 1건 이상 발견된다.
|
||||
2. Given `grep -n "POST /auth/tokens" docs/interface-contract.md`, When 본 REQ 구현 후, Then client token 발급 명세가 제거되어 0건이거나 deprecated 표시로만 잔존한다.
|
||||
3. Given `grep -n "AuthContext.*daemon_id\|daemon_id.*AuthContext" docs/runtime-flows.md`, When 본 REQ 구현 후, Then `AuthContext` 필드 목록에 `daemon_id`가 명시된다.
|
||||
4. Given `grep -n "revoked_at IS NULL\|active service token" docs/data-model.md`, When 본 REQ 구현 후, Then `idx_auth_tokens_service_name_unique` 변경 사항이 반영되어 발견된다.
|
||||
5. Given 사람 리뷰, When baseline 4종 문서를 점검, Then client token 잔재(예: `token_type='client'` 발급 경로 언급)가 모두 제거되거나 deprecated 표시되어 있다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md`
|
||||
- `docs/runtime-flows.md`
|
||||
- `docs/data-model.md`
|
||||
- `docs/project-structure.md` (필요 시)
|
||||
- Modules: 없음 (문서 전용)
|
||||
- Tests: 없음 (문서 전용. AC-1~5는 grep + 사람 리뷰)
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 REQ-016/017/018/022/023 코드 변경 후 또는 동일 batch에서 함께 처리하는 것이 적절하다. baseline 문서가 코드 변경 사항을 반영하지 않으면 후속 REQ/batch가 잘못된 source of truth를 참조한다.
|
||||
- 본 REQ는 surface 정합성 회복만 다루고 신규 정책 명세는 다음 REQ에서 처리:
|
||||
- REQ-020: 토큰 발급 권한 매트릭스
|
||||
- REQ-021: 토큰 만료·회전 정책
|
||||
- REQ-024: env admin token 운영 SOP
|
||||
- Source: dcy-004 FR-4, dcy-004 FR-10.
|
||||
|
||||
## 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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-4 + FR-10 통합 산출물.
|
||||
|
||||
#### Intent
|
||||
- 토큰 surface baseline 4종 문서의 정합성 회복(rebind 명세 누락, AuthContext daemon_id 누락, client token 잔재 제거, auth_tokens 인덱스 변경 반영).
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md, docs/runtime-flows.md, docs/data-model.md, docs/project-structure.md — 최초 지정
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### 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-016/017/018/022/023 코드 변경 후 또는 동일 batch에서 처리
|
||||
|
||||
#### Validation Plan
|
||||
- grep 명령 + 사람 리뷰로 AC-1~5 검증
|
||||
@@ -0,0 +1,93 @@
|
||||
# REQ-020: 토큰 발급 권한 매트릭스 정의
|
||||
|
||||
- Status: Approved
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 누가 어떤 token type을 발급/회전/폐기할 수 있는가가 현재 코드에 흩어진 dependency(require_admin/require_read)로만 표현되고 baseline 문서에 없어 운영 일관성과 정책 추적이 어렵다. 토큰 발급 권한 매트릭스를 baseline에 명시해 정책 source of truth를 확보한다.
|
||||
|
||||
## Requirement
|
||||
- `docs/interface-contract.md`에 "토큰 발급 권한 매트릭스" 섹션을 추가한다. 매트릭스 차원: token type(session / service / env) × 발급 동사(issue / rotate / rebind / revoke / list) × 발급자 권한(admin / trade-write / read).
|
||||
- client token은 REQ-016에서 폐지되므로 표에서 제거한다.
|
||||
- admin 자기 발급 정책 명시: admin이 본인 user_id 대상으로 daemon-bound 또는 unbound service token 발급 가능(dcy-004 OQ-4 결정). 매트릭스 주석에 "관행상 최소 권한 발급 권장" 명시.
|
||||
- 매트릭스 위반 호출은 HTTP 403 fail-closed로 통일한다(dcy-004 NFR-4).
|
||||
- 매트릭스 정의는 현행 코드 dependency(require_admin/require_read, AuthService 발급 메서드의 분기 로직)와 1:1 매핑되어야 한다. 불일치 시 본 REQ가 우선이며 코드 보정은 REQ-018 또는 별도 후속에서 처리.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `docs/interface-contract.md` 검토, When 본 REQ 구현 후, Then "토큰 발급 권한 매트릭스" 섹션이 존재하고 token type 3종(session/service/env) × 발급 동사 5종(issue/rotate/rebind/revoke/list) × 발급자 권한 3종(admin/trade-write/read) 조합이 모두 명시된다.
|
||||
2. Given 매트릭스의 admin 자기 발급 정책 항목, When 본 REQ 구현 후 검토, Then admin이 본인 user_id로 service token 발급 가능함이 명시되고 최소 권한 권장 주석이 포함된다.
|
||||
3. Given 매트릭스 위반 호출 정책, When 본 REQ 구현 후 검토, Then 모든 위반 호출에 HTTP 403 fail-closed 응답이 명시된다.
|
||||
4. Given 매트릭스와 현행 코드 dependency(`require_admin`/`require_read`) 매핑, When 사람 리뷰, Then 1:1 매핑이 확인되고 불일치 항목은 별도 follow-up으로 기록된다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — "토큰 발급 권한 매트릭스" 섹션 신규 추가
|
||||
- Modules: 없음 (문서 전용. 코드 dependency 불일치 발견 시 별도 REQ 또는 REQ-018 보강)
|
||||
- Tests: 없음 (문서 전용. NFR-4 위반 호출 403 검증은 REQ-016/018/022 등 코드 REQ의 회귀 테스트에 포함)
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 매트릭스 baseline 문서화만 다룬다. 매트릭스 위반 호출의 코드 레벨 enforcement는 기존 dependency 체계로 충분하며 본 REQ에서는 정책 정의만 한다.
|
||||
- env admin token의 회전 주기·강도 가이드는 REQ-024(env admin SOP)가 별도로 다룬다. 본 REQ는 발급 권한 매트릭스에만 집중한다.
|
||||
- Source: dcy-004 FR-5, dcy-004 NFR-4, dcy-004 OQ-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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-5 산출물.
|
||||
|
||||
#### Intent
|
||||
- 토큰 발급 권한 정책 source of truth 확보 + admin 자기 발급 허용 정책 baseline 명시.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~4 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md — 최초 지정
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### 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-019 baseline 정합 작업과 동일 batch 권장
|
||||
|
||||
#### Validation Plan
|
||||
- 사람 리뷰로 AC-1~4 검증; 매트릭스-코드 dependency 매핑 사전 검토
|
||||
@@ -0,0 +1,98 @@
|
||||
# REQ-021: 토큰 만료·회전 정책 정의
|
||||
|
||||
- Status: Approved
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 현행 토큰 만료 정책이 코드에 흩어져 있고(session은 settings.session_token_ttl_hours, service는 365일 default, `--no-expiry` 옵션) baseline 정책 명세가 없어 운영 일관성이 떨어진다. 토큰 type별 기본 TTL과 `no_expiry` 허용 범위·강제 회전 정책을 baseline에 명시한다.
|
||||
- dcy-004 OQ-5 결정에 따라 현행 TTL 유지로 시작하되 정책 표면을 명시해 후속 변경 추적 기반을 확보한다.
|
||||
|
||||
## Requirement
|
||||
- `docs/runtime-flows.md`(또는 `docs/interface-contract.md`)에 "토큰 만료·회전 정책" 섹션을 추가한다. 명시 항목:
|
||||
- **type별 기본 TTL**: session = `settings.session_token_ttl_hours`(현행 값 유지), service = 365일 default, env = 만료 없음
|
||||
- **no_expiry 허용 범위**: service token 한정. session/(client는 폐지됨) 발급 경로는 모두 거부
|
||||
- **강제 회전 주기**: 본 정책에서는 강제 주기 두지 않음. env admin token의 권장 회전 주기는 REQ-024(env admin SOP)에서 별도 정의
|
||||
- **정책 위반 발급 처리**: `no_expiry`를 session에 요청하면 HTTP 422 또는 ValueError로 거부
|
||||
- 정책 명세는 현행 코드 구현과 일치해야 한다(`AuthService.login`, `AuthService.issue_service_token`의 만료 처리 로직 reference 명시).
|
||||
- 본 REQ는 정책 baseline 명시에 집중하고 코드 enforcement(no_expiry session 거부 등)가 누락된 경우 follow-up REQ로 분리한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `docs/runtime-flows.md` 검토, When 본 REQ 구현 후, Then "토큰 만료·회전 정책" 섹션이 존재하고 type별 기본 TTL이 명시된다.
|
||||
2. Given 정책 검토, When `no_expiry` 허용 범위 항목을 확인, Then service 한정으로 명시되고 session/client는 거부됨이 명시된다.
|
||||
3. Given 정책 검토, When 강제 회전 주기 항목을 확인, Then 본 정책에서는 두지 않으며 env admin은 REQ-024에서 별도 정의됨이 명시된다.
|
||||
4. Given 정책 위반 발급 시도(예: `no_expiry`를 session에 요청), When 정책 명세 검토, Then HTTP 422 또는 ValueError 거부 응답이 명시된다.
|
||||
5. Given 정책 명세와 현행 코드 매핑, When `app/services/auth_service.py`의 `login`(session 만료)·`issue_service_token`(service 만료·no_expiry) 분기와 비교, Then 정책 본문이 코드와 일치하거나 불일치 항목이 follow-up으로 기록된다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/runtime-flows.md` — "토큰 만료·회전 정책" 섹션 신규 추가 (또는 `docs/interface-contract.md`에 추가 후 runtime-flows에서 참조)
|
||||
- Modules: 없음 (정책 baseline 문서 정의. 코드 enforcement는 현행 그대로 유지)
|
||||
- Tests: 없음 (문서 전용. AC-4 정책 위반 거부 회귀는 향후 코드 변경 시점에 추가)
|
||||
|
||||
## Notes
|
||||
- dcy-004 OQ-5 결정으로 현행 TTL 유지가 채택됐다. 본 REQ는 정책 명세 baseline화를 통해 후속 변경 추적 기반을 확보한다.
|
||||
- session 만료 시 in-flight 거래 영향 같은 정책 세부 사항은 본 REQ 범위 밖이며 release plan 또는 별도 운영 Discovery에서 다룬다.
|
||||
- 강제 회전 주기(분기/반기/연간)는 env admin token에 한정해 REQ-024에서 다루며, session/service에는 강제 주기를 두지 않는다.
|
||||
- Source: dcy-004 FR-6, dcy-004 OQ-5 결정.
|
||||
|
||||
## 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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-6 산출물.
|
||||
|
||||
#### Intent
|
||||
- 토큰 만료 정책 source of truth 확보 + 현행 TTL 유지 정책 baseline 명시.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/runtime-flows.md — 최초 지정
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### 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-019/020과 동일 batch 권장
|
||||
|
||||
#### Validation Plan
|
||||
- 사람 리뷰로 AC-1~5 검증
|
||||
@@ -0,0 +1,107 @@
|
||||
# REQ-024: env admin token 운영 SOP 정의
|
||||
|
||||
- Status: Approved
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 현행 env admin token(`PORTFOLIOHUB_ADMIN_TOKENS` 환경 변수)의 회전·폐기·강도 기준이 baseline에 명시되지 않아 운영 단계에서 회전이 잘못 적용되면 운영자 admin 접근이 차단될 수 있는 운영 리스크가 있다(dcy-004 R-4).
|
||||
- dcy-004 OQ-6 결정에 따라 분기(3개월) 권장 회전 주기, 강제 회전 없음 정책으로 SOP를 baseline에 명시한다.
|
||||
|
||||
## Requirement
|
||||
- `docs/runtime-flows.md` 또는 `README.md`에 "env admin token 운영 SOP" 섹션을 추가한다. 명시 항목:
|
||||
- **권장 회전 주기**: 분기(3개월). 강제 회전·만료 알람 없음. 운영자 자율로 회전 시점 결정.
|
||||
- **회전 트리거 조건**: (a) 정기(분기), (b) token leak 의심, (c) 운영자 교체·퇴사, (d) 보안 사고 대응.
|
||||
- **다단계 회전 절차**:
|
||||
1. 강한 무작위 token 생성: `python3 -c "import secrets; print(secrets.token_urlsafe(48))"`
|
||||
2. `PORTFOLIOHUB_ADMIN_TOKENS`에 신규 token을 콤마 분리로 추가(다중 token 동시 운영)
|
||||
3. service rolling restart
|
||||
4. 신규 token으로 admin 호출 검증(예: `GET /health`, admin 작업 sanity check)
|
||||
5. 구 token을 `PORTFOLIOHUB_ADMIN_TOKENS`에서 제거
|
||||
6. service rolling restart(구 token 무효화)
|
||||
7. evidence 기록(회전 일자, 운영자, 신규 token 강도 확인, sanity check 결과)
|
||||
- **rollback 절차**: 회전 실패 시 환경 변수에 구 token 재추가 후 rolling restart. fallback admin 접근 경로(예: DB 직접 접근으로 token 재생성) 보장.
|
||||
- **운영자 권한 매트릭스**: env admin token 추가/제거는 인프라 운영자 권한. release approver 또는 운영자 교체 시 SOP를 참조.
|
||||
- **token 강도 가이드**: 최소 48-byte URL-safe 무작위. 평문 단어·예측 가능한 패턴(예: `admin-token-789`) 금지.
|
||||
- SOP는 dev/test 환경과 prod 환경 모두에 적용되며, prod 환경의 약한 placeholder token이 발견된 경우(R-7) 본 SOP를 즉시 적용하도록 권고한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `docs/runtime-flows.md` 또는 `README.md` 검토, When 본 REQ 구현 후, Then "env admin token 운영 SOP" 섹션이 존재하고 권장 회전 주기 + 강제 회전 없음 정책이 명시된다.
|
||||
2. Given SOP의 다단계 회전 절차 항목, When 검토, Then 7단계(token 생성 → 추가 → restart → 검증 → 구 token 제거 → restart → evidence 기록)가 명시된다.
|
||||
3. Given SOP의 rollback 절차 항목, When 검토, Then 회전 실패 시 환경 변수 복구 + fallback admin 접근 경로 명시.
|
||||
4. Given SOP의 token 강도 가이드, When 검토, Then 최소 48-byte URL-safe 무작위 요구 + 약한 placeholder 금지 명시.
|
||||
5. Given staging 환경에서 SOP에 따라 dry-run 회전 실행, When 7단계 절차 수행, Then 각 단계가 실제 환경에서 적용 가능함이 확인된다(release plan verification).
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/runtime-flows.md` — "env admin token 운영 SOP" 섹션 신규 추가 (또는 `README.md`에 추가 후 runtime-flows에서 참조)
|
||||
- `README.md` — daemon provisioning runbook에 env admin token SOP 링크 추가 (선택)
|
||||
- Modules: 없음 (정책 baseline 문서. 코드 변경 없음. 환경 변수 자체는 운영 작업)
|
||||
- Tests: 없음 (문서 전용. AC-5는 release plan verification 영역)
|
||||
|
||||
## Notes
|
||||
- dcy-004 R-7(prod 환경 약한 admin token)은 본 SOP의 첫 번째 적용 사례로, dcy-004 confirm-discovery 시점 또는 이전에 운영자가 본 SOP의 7단계 절차를 적용해 `admin-token-789`를 강한 무작위 값으로 교체하기를 권고. hotfix 추적은 dcy-004 §10 DATA_PROD_ADMIN_TOKEN_HOTFIX 항목으로 관리.
|
||||
- env admin token 다중 운영 허용은 현행 `app/core/auth.py:_resolve_permission`이 set 기반으로 처리하므로 코드 변경 없이 가능([app/core/auth.py:43-50](app/core/auth.py#L43-L50)).
|
||||
- 본 SOP는 prod 환경 적용을 우선으로 작성됐으나 dev/test 환경에서도 약한 placeholder 사용을 금지한다. dev 환경 사용자는 `secrets.token_urlsafe` 또는 동등한 강도의 값을 생성해 .env 파일에 보관.
|
||||
- Source: dcy-004 FR-9, dcy-004 OQ-6 결정.
|
||||
|
||||
## 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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-9 산출물.
|
||||
|
||||
#### Intent
|
||||
- env admin token 운영 SOP source of truth 확보 + 분기 권장 회전 주기 baseline 명시 + dcy-004 R-7 hotfix 표준 절차 정의.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/runtime-flows.md, README.md — 최초 지정
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정). dcy-004 R-7 hotfix는 본 REQ confirm-req 이전이라도 본 SOP 절차로 즉시 적용 권장.
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입; REQ-019/020/021과 동일 batch 권장
|
||||
|
||||
#### Validation Plan
|
||||
- 사람 리뷰로 AC-1~4 검증; AC-5는 staging 환경 dry-run으로 release plan 검증
|
||||
@@ -0,0 +1,116 @@
|
||||
# REQ-032: portfolio v1 baseline 문서 갱신
|
||||
|
||||
- Status: Approved
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- REQ-025~031의 v1 portfolio mutation 라우트 신설 + path 분리 + soft delete + 토큰 분기 정책을 baseline 문서(`docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/data-model.md`, `docs/project-structure.md`)에 일괄 반영해 후속 batch/release가 단일 source of truth로 사용할 수 있도록 정합성을 회복한다.
|
||||
- dcy-001 FR-5(REQ-013) Deprecated 후속 정리도 함께 반영 — `GET /v1/assets`는 catalog 전용 의미로 baseline에 복원.
|
||||
|
||||
## Requirement
|
||||
- `docs/interface-contract.md` 갱신:
|
||||
- `/v1/portfolios*` 라우트 명세 추가: POST(REQ-025), PATCH(REQ-026), DELETE(REQ-027), GET(REQ-028), `/ledger/trades`(REQ-029), `/tags`(REQ-030), `/comments`(REQ-031)
|
||||
- 각 라우트의 토큰 분기 규칙(`token_type ∈ {service, client}` 거부 + `permission` 기준 분기) 일관 명시
|
||||
- 구 path `POST /v1/assets/{asset_id}/ledger/trades` 제거 + 동일 batch release 정책 명시
|
||||
- `GET /v1/assets`는 dcy-001 FR-2 admin catalog 조회 전용으로 의미 복원 (REQ-013 Deprecated 정합)
|
||||
- daemon authorization model 갱신 (3단계 fail-closed)
|
||||
- `docs/runtime-flows.md` 갱신:
|
||||
- portfolio mutation flow(create + catalog 검증 + ownership 검증 + disabled 보호)
|
||||
- GET /v1/portfolios 토큰 분기 조회 flow
|
||||
- ledger trade path 이전 흐름 + daemon assignment 3단계 인가
|
||||
- soft delete flow (status 전환 + daemon 자동 해제 + 의존 데이터 보존 + 복구 경로)
|
||||
- `docs/data-model.md` 갱신:
|
||||
- Portfolio 엔티티의 `status` 필드 `disabled` 의미 명시 + 의존 데이터 보존 정책
|
||||
- dcy-001 catalog/portfolio 분리 모델 baseline 반영(REQ-009/011 정합 — dcy-001 REQ-015에서 처리될 수 있음, 본 REQ는 portfolio v1 측면만 보강)
|
||||
- `docs/project-structure.md` 갱신:
|
||||
- `app/api/v1_portfolios.py` 신규 파일 추가 + 라우터 책임 갱신
|
||||
- `app/api/v1_ledger.py` 제거 또는 v1_portfolios.py에 흡수 반영
|
||||
- 갱신은 v1 portfolio mutation surface 정합성 회복에 집중한다. dcy-002 tag 정책의 baseline 반영은 dcy-002 REQ-008이 별도로 다루며, 본 REQ는 portfolio tag 적용 라우트 명세만 포함한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `grep -n "POST /v1/portfolios\|PATCH /v1/portfolios\|DELETE /v1/portfolios\|GET /v1/portfolios" docs/interface-contract.md`, When 본 REQ 구현 후, Then 각 라우트 명세 entry가 1건 이상 발견된다.
|
||||
2. Given `grep -n "POST /v1/assets/{asset_id}/ledger/trades" docs/interface-contract.md`, When 본 REQ 구현 후, Then 구 path 명세가 제거되어 0건이거나 "deprecated/removed" 표시로만 잔존.
|
||||
3. Given `grep -n "GET /v1/portfolios.*?market\|managed_by_daemon_id" docs/runtime-flows.md`, When 본 REQ 구현 후, Then `GET /v1/portfolios` 토큰 분기 flow + daemon-scoped 필터가 명시된다.
|
||||
4. Given `grep -n "soft delete\|status='disabled'\|disabled" docs/data-model.md`, When 본 REQ 구현 후, Then Portfolio.status='disabled' 의미와 의존 데이터 보존 정책이 반영된다.
|
||||
5. Given `grep -n "v1_portfolios.py" docs/project-structure.md`, When 본 REQ 구현 후, Then 신규 라우터 파일이 명시된다.
|
||||
6. Given 사람 리뷰, When baseline 4종 문서를 점검, Then REQ-025~031의 v1 portfolio mutation 정책과 충돌하는 잔재가 없다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md`
|
||||
- `docs/runtime-flows.md`
|
||||
- `docs/data-model.md`
|
||||
- `docs/project-structure.md`
|
||||
- Modules: 없음 (문서 전용)
|
||||
- Tests: 없음 (문서 전용. AC-1~6은 grep + 사람 리뷰)
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 REQ-025~031 코드 변경 후 또는 동일 batch에서 함께 처리하는 것이 적절하다. baseline 문서가 코드 변경 사항을 반영하지 않으면 후속 REQ/batch가 잘못된 source of truth를 참조한다.
|
||||
- dcy-004 REQ-019(토큰 surface baseline 정합성)와 본 REQ는 baseline 갱신 대상이 부분적으로 겹치나 책임 분담:
|
||||
- REQ-019: 토큰 surface (client 폐지, rebind, AuthContext daemon_id, auth_tokens 인덱스)
|
||||
- 본 REQ-032: portfolio v1 surface (path 분리, soft delete, ownership/disabled, ledger path 이전)
|
||||
- 두 REQ가 동일 batch에서 처리될 경우 baseline 문서 동시 갱신 시 conflict 회피를 위해 commit 순서 또는 작업자 분담 명시 필요.
|
||||
- dcy-001 REQ-015(asset catalog baseline)도 baseline 갱신 대상이라 batch design 단계에서 3개 REQ(REQ-015 + REQ-019 + REQ-032)의 baseline 작업을 통합 batch로 묶거나 의존성 순서를 명시.
|
||||
- Source: dcy-003 FR-10. dcy-001 REQ-013 Deprecated 후속 baseline 정리 포함.
|
||||
|
||||
## 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-10 산출물.
|
||||
|
||||
#### Intent
|
||||
- v1 portfolio mutation surface의 baseline 문서 정합성 회복 + dcy-001 REQ-013 Deprecated 후속 정리.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~6 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md, docs/runtime-flows.md, docs/data-model.md, docs/project-structure.md — 최초 지정
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### 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~031 코드 변경 후 또는 동일 batch에서 처리. dcy-004 REQ-019, dcy-001 REQ-015와의 baseline 작업 충돌 조율 필요.
|
||||
|
||||
#### Validation Plan
|
||||
- grep + 사람 리뷰로 AC-1~6 검증
|
||||
@@ -47,7 +47,7 @@
|
||||
- `tests/test_asset_catalog.py` (신규) — admin CRUD CLI/API AC 검증, non-admin 403 검증
|
||||
|
||||
## Notes
|
||||
- `pfhub asset list`의 HTTP 대응(catalog 전용 GET)은 별도 admin 전용 엔드포인트로 구현하거나, GET /v1/assets에 `?mode=catalog` 파라미터로 분기할 수 있다. 설계 결정은 batch design 단계에서 확정한다. REQ-013(GET /v1/assets 포트폴리오 조회)과 충돌하지 않도록 주의한다.
|
||||
- `pfhub asset list`의 HTTP 대응(catalog 전용 GET): dcy-003 옵션 B(path 명명 분리) 결정에 따라 REQ-013(GET /v1/assets 포트폴리오 조회)이 Deprecated(2026-05-20) 처리되어 path 충돌 우려가 해소됨. **`GET /v1/assets`는 admin catalog 조회 전용**으로 본 REQ에서 다루며, dcy-001 batch design 단계에서 본 REQ에 GET 명세를 보강(change-req)하거나 별도 신규 REQ로 분리한다. 포지션 조회 `GET /v1/portfolios`는 dcy-003의 별도 신규 REQ로 분리 예정.
|
||||
- deactivate 후 해당 asset을 참조하는 기존 portfolio는 보존된다. 신규 portfolio 생성만 거부(REQ-012에서 처리).
|
||||
- REQ-009 선행 필요.
|
||||
- Source: dcy-001 FR-2, NFR-3
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# REQ-013: GET /v1/assets 토큰 타입별 분기 재정의
|
||||
|
||||
- Status: Approved
|
||||
- Status: Deprecated
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
- Deprecated By: dcy-003 (path 명명 분리 옵션 B 결정), 2026-05-20
|
||||
|
||||
## Intent
|
||||
- 현재 service token(daemon) 전용으로 구현된 `GET /v1/assets`를 user token 및 admin token도 지원하는 통합 portfolio 조회 엔드포인트로 재정의한다.
|
||||
@@ -46,6 +47,44 @@
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260520-01
|
||||
- Date: 2026-05-20
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- Status: Approved → **Deprecated**. dcy-003(`docs/discovery/dcy-003_20260520_user-portfolio-mutation-v1-api-migration.md`, confirmed 2026-05-20)의 옵션 B(완전 분리) 결정으로 본 REQ의 path 결정이 무효화됨.
|
||||
|
||||
#### Intent
|
||||
- dcy-003은 `GET /v1/assets`를 admin asset catalog 전용으로 회수하고 포지션 조회는 `GET /v1/portfolios` 신설 경로로 분리하는 정책을 채택했다. 본 REQ의 통합 endpoint 정의(`GET /v1/assets`가 토큰 타입별로 catalog/포지션을 동시 처리)는 dcy-003 결정에 따라 더 이상 기준 문서가 아니므로 Deprecated로 전환한다.
|
||||
- 본 REQ의 후속:
|
||||
- `GET /v1/assets` (admin catalog 조회 의미) → dcy-001 batch design 단계에서 REQ-010 또는 신규 REQ로 분리 처리 (REQ-010 Notes 참조)
|
||||
- `GET /v1/portfolios` (포지션 토큰 분기 조회) → dcy-003 FR-5에서 별도 신규 REQ로 작성 예정 (dcy-003 draft-req 단계)
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Removed: AC-1~7 전체 (본 REQ Deprecated로 인해 검증 대상에서 제외). 후속 신규 REQ에서 새 path 기준으로 재작성 예정.
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- docs/discovery/dcy-003 §6 OQ-5, §10 CONFIRM_DCY001_FR5_CHANGE_REQ가 본 Deprecated 처리의 근거
|
||||
- docs/srs/Interface/req-010 Notes에 후속 GET /v1/assets catalog 명세 처리 계획 반영
|
||||
- Modules:
|
||||
- 본 REQ Approved 시점에 코드 변경이 일어나지 않은 상태였으므로 코드 회귀 없음. 후속 신규 REQ에서 `app/api/v1_portfolios.py` 신규 + `app/api/v1_assets.py` 정리.
|
||||
- Tests:
|
||||
- 신규 `tests/test_v1_asset_catalog.py`(REQ-013 Notes에 지정)는 작성되지 않은 상태. 후속 신규 REQ로 이관.
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: 본 REQ가 포함된 batch 미정. Deprecated 이후 batch 편입 없음.
|
||||
- Releases: 본 REQ 포함 release 미정.
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no (구현 미진행 상태에서 Deprecated)
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: deprecated
|
||||
- Follow-up Action: dcy-003 draft-req 단계에서 `GET /v1/portfolios` 신규 REQ 작성, dcy-001 batch design 단계에서 `GET /v1/assets` catalog 조회 명세 처리(REQ-010 보강 또는 신규 REQ)
|
||||
|
||||
#### Validation Plan
|
||||
- 없음 (Deprecated 전환만)
|
||||
|
||||
### CHG-20260519-02
|
||||
- Date: 2026-05-19
|
||||
- Author: Justin Ko
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# REQ-016: client token 발급 경로 코드 폐지
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- dcy-003 옵션 B 결정으로 client token이 v1 portfolio mutation에서 거부되며, dcy-004 OQ-8 결정으로 user 자동화 use case는 admin 발급 service token으로 흡수된다. client token 발급 경로 자체를 코드에서 폐지해 토큰 surface를 session/service/env 3종으로 축소하고 발급 경로 중복을 제거한다.
|
||||
- DATA-1 inventory(2026-05-20) active client token 0건 확인됐으므로 즉시 코드 폐지로 진행하며 별도 revoke 스크립트나 grace period 운영 없이 release 단계에서 직접 제거한다.
|
||||
|
||||
## Requirement
|
||||
- `POST /auth/tokens` HTTP route([app/api/auth_routes.py:100-122](app/api/auth_routes.py#L100-L122)), `app/services/auth_service.py:issue_client_token`, `app/schemas/auth.py:CreateClientTokenRequest`/`IssuedClientTokenResponse`, 관련 헬퍼·테스트를 모두 제거한다.
|
||||
- HTTP 호출 시 라우트가 등록되어 있지 않아 FastAPI 기본 404 응답을 반환하도록 한다(별도 405 처리 추가하지 않음).
|
||||
- 폐지 release 직전 `SELECT COUNT(*) FROM auth_tokens WHERE token_type='client' AND revoked_at IS NULL` 재확인을 release plan에 포함하며, >0이면 발급 사유 조사 후 release 보류 또는 즉시 revoke 처리한다.
|
||||
- 토큰 분기에서 `token_type='client'`는 더 이상 발급될 수 없으므로 dcy-003 FR-1/3/4/5의 게이트는 자연 유효한 상태로 유지되되 게이트 자체는 제거하지 않는다(env token 침투 방어).
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given codebase grep `grep -rn "issue_client_token\|CreateClientTokenRequest\|IssuedClientTokenResponse"`, When 본 REQ 구현 후, Then 결과는 0건이다.
|
||||
2. Given `POST /auth/tokens` HTTP 호출, When 본 REQ 구현 후, Then HTTP 404(또는 FastAPI 기본 not-found 응답)가 반환된다.
|
||||
3. Given 폐지 release 직전 DATA-1 재확인 쿼리, When 실행, Then `COUNT = 0` 결과가 release plan evidence로 첨부된다.
|
||||
4. Given dcy-003 신규 REQ들의 token_type 게이트 회귀 테스트, When 본 REQ 구현 후 재실행, Then 모두 통과한다(client token 발급 불가에 따른 회귀 없음).
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — POST /auth/tokens 명세 제거 (REQ-019 baseline 정합 작업에 포함)
|
||||
- Modules:
|
||||
- `app/api/auth_routes.py` — POST /auth/tokens 라우트, CreateClientTokenRequest/IssuedClientTokenResponse import, `create_token`/`revoke_token` 중 client 의존 부분 제거
|
||||
- `app/services/auth_service.py` — `issue_client_token`, `IssuedClientToken` 데이터 클래스 제거. 단, `list_tokens`/`revoke_token` 등이 client 토큰을 함께 다루는 부분은 token_type 분기 정리 검토
|
||||
- `app/schemas/auth.py` — `CreateClientTokenRequest`, `IssuedClientTokenResponse` 제거
|
||||
- Tests:
|
||||
- 신규 `tests/test_auth_surface_after_client_deprecation.py` — AC-1/AC-2/AC-4 검증
|
||||
- 기존 `tests/test_auth.py` 중 client token 관련 케이스 제거 또는 service token 케이스로 대체
|
||||
|
||||
## Notes
|
||||
- DATA-1 inventory(2026-05-20) active client token 0건 확인됨. 발급 보유자에 대한 별도 통보 절차 불요.
|
||||
- 토큰 surface 3종(session/service/env)으로 축소 후 user 자동화 use case는 admin 발급 service token으로 흡수(dcy-004 OQ-8 결정).
|
||||
- dcy-003 OQ-5 결정의 token_type 게이트는 본 REQ 구현 후에도 코드에 유지(env token 외부 침투 방어).
|
||||
- Source: dcy-004 FR-1, dcy-004 NFR-1, dcy-003 OQ-5 후속.
|
||||
|
||||
## 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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-1 (client token 발급 경로 코드 폐지) 산출물.
|
||||
|
||||
#### Intent
|
||||
- 토큰 surface 3종 축소 + client 폐지 정책 코드 반영.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~4 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md (REQ-019에서 처리)
|
||||
- Modules: app/api/auth_routes.py, app/services/auth_service.py, app/schemas/auth.py — 최초 지정
|
||||
- Tests: tests/test_auth_surface_after_client_deprecation.py (신규), 기존 tests/test_auth.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 계획 편입; release 직전 DATA-1 재확인 절차 포함
|
||||
|
||||
#### Validation Plan
|
||||
- pytest로 AC-1~4 검증; codebase grep + HTTP 404 회귀 + dcy-003 token_type 게이트 회귀
|
||||
@@ -0,0 +1,97 @@
|
||||
# REQ-018: POST /auth/service-tokens body daemon_id forward 보정
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 현행 `POST /auth/service-tokens` HTTP route([app/api/auth_routes.py:131-137](app/api/auth_routes.py#L131-L137))가 `IssueServiceTokenRequest.daemon_id` 필드를 스키마에서 노출하지만 `service.issue_service_token` 호출 시 forward하지 않아 silently drop되는 결함을 정상화한다.
|
||||
- CLI `pfhub service-token issue --daemon-id`는 정상 forward하나 HTTP 경로만 daemon binding이 불가능한 상태였다. HTTP/CLI 일관성을 회복한다.
|
||||
|
||||
## Requirement
|
||||
- `app/api/auth_routes.py:131-137`의 `service.issue_service_token(...)` 호출에 `daemon_id=payload.daemon_id`를 추가한다.
|
||||
- 비존재 또는 비활성 daemon_id가 body로 전달되면 `AuthService.issue_service_token`의 기존 검증 로직(`get_daemon_by_id` None 또는 `is_active=False`)이 ValueError를 발생시키고, `_raise_auth_error`를 통해 HTTP 4xx로 변환된다.
|
||||
- daemon_id 검증 실패 응답 코드는 ValueError detail에 따라 HTTP 400 또는 404로 분기된다(`_raise_auth_error` 기존 로직).
|
||||
- 본 REQ는 HTTP body daemon_id 처리만 보정한다. CLI 동작, daemon registry 자체, service token 발급 정책은 변경하지 않는다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given admin token, When `POST /auth/service-tokens` body `{"name": "...", "username": "...", "daemon_id": "<existing-active-daemon-id>"}` 호출, Then HTTP 201 + issued token의 `auth_token.daemon_id == <daemon-id>` 반환.
|
||||
2. Given admin token, When `POST /auth/service-tokens` body `{"name": "...", "username": "...", "daemon_id": "<non-existing-daemon-id>"}` 호출, Then HTTP 4xx + `_raise_auth_error` 패턴에 따른 오류 메시지 반환.
|
||||
3. Given admin token, When `POST /auth/service-tokens` body `daemon_id` 필드 미포함 또는 null, Then 기존 동작 유지(daemon binding 없는 service token 발급, HTTP 201).
|
||||
4. Given AC-1과 CLI `pfhub service-token issue NAME --user U --daemon-id <daemon-id>` 실행 결과 비교, When 둘 다 발급된 토큰의 `daemon_id` 확인, Then 동일한 값이 저장된다(HTTP/CLI 동등성).
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — POST /auth/service-tokens body 명세에 daemon_id forward 동작 추가 (REQ-019에서 처리)
|
||||
- Modules:
|
||||
- `app/api/auth_routes.py:131-137` — `issue_service_token` 호출 시 `daemon_id=payload.daemon_id` 추가
|
||||
- Tests:
|
||||
- 갱신 `tests/test_auth.py` — AC-1~3 HTTP request 시뮬레이션 추가
|
||||
- 갱신 `tests/test_daemon_registry.py` — AC-4 HTTP/CLI 동등성 회귀
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 1-line 보정에 가까운 단순 결함 수정이지만, HTTP API 사용자에게는 daemon binding 경로가 새로 작동하기 시작하는 의미 있는 변경이라 별도 REQ로 분리.
|
||||
- daemon registry 자체(존재/활성 검증)는 기존 `AuthService.issue_service_token` 내부 로직이 그대로 처리하므로 본 REQ에서는 daemon 모델을 건드리지 않는다.
|
||||
- HTTP body daemon_id를 누락하거나 null로 보내는 경우의 기본 동작(unbound service token 발급)은 dcy-004 OQ-7에서 "수동 매핑" 정책이 결정되었으므로 본 REQ에서 추가 제한하지 않는다. NULL daemon 토큰의 운영 정리는 REQ-023(마이그레이션 CLI)에서 처리.
|
||||
- Source: dcy-004 FR-3.
|
||||
|
||||
## 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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-3 산출물.
|
||||
|
||||
#### Intent
|
||||
- HTTP/CLI 토큰 발급 surface 일관성 회복 (daemon_id forward 누락 결함 정상화).
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~4 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md (REQ-019에서 처리)
|
||||
- Modules: app/api/auth_routes.py — 최초 지정
|
||||
- Tests: tests/test_auth.py, tests/test_daemon_registry.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 계획 편입
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~4 검증; HTTP/CLI 동등성 회귀
|
||||
@@ -0,0 +1,103 @@
|
||||
# REQ-022: daemon deactivation 시 바인딩 service token 자동 revoke
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 현재 `pfhub daemon update --deactivate`가 daemon `is_active=false`만 설정하고 바인딩된 service token에 영향이 없어 비활성 daemon의 토큰이 계속 유효한 상태로 남는 운영 공백을 해소한다.
|
||||
- dcy-004 OQ-3 결정에 따라 deactivation 즉시 바인딩 service token 모두 revoke해 daemon 권한 종료를 token 단계까지 즉각 반영한다.
|
||||
|
||||
## Requirement
|
||||
- `pfhub daemon update <daemon_id> --deactivate` 실행 시(또는 동등 service 메서드 호출 시) 동일 트랜잭션 내에서 `auth_tokens` 중 `daemon_id == <daemon_id>` AND `revoked_at IS NULL`인 모든 row를 revoke(`revoked_at = NOW()`)한다.
|
||||
- 회피 옵션(예: `--keep-tokens`)은 노출하지 않는다(정책 일관성).
|
||||
- graceful drain은 daemon 측 책임으로 분리한다. release plan에 "(1) daemon 프로세스 종료/drain 완료 확인 → (2) `pfhub daemon update --deactivate` 실행 → (3) 자동 revoke" 순서로 SOP를 명시한다(REQ-019 baseline 작업에 포함).
|
||||
- `app/cli/commands/daemon.py:update_daemon`이 `deactivate=True`로 호출될 때 `repository` 또는 신규 service 메서드(`revoke_tokens_for_daemon(daemon_id)`)를 통해 자동 revoke를 트리거한다.
|
||||
- 재활성화(`--activate`)는 자동 token 재발급이나 unrevoke를 수행하지 않는다. 운영자가 별도 발급 또는 rebind를 수행해야 한다.
|
||||
- 본 REQ는 v1 API의 daemon 모델([app/domain/models.py:Daemon](app/domain/models.py))과 service token 모델 위에서 작동하며 daemon registry 자체는 변경하지 않는다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given active daemon에 바인딩된 active service token 1개 이상 존재, When `pfhub daemon update <daemon_id> --deactivate --force` 실행, Then 해당 daemon에 바인딩된 모든 service token의 `revoked_at`가 NOT NULL로 갱신되고 daemon `is_active=false`로 설정된다.
|
||||
2. Given AC-1 실행 후, When revoke된 token으로 v1 API 호출(예: `GET /v1/portfolios` 또는 `GET /v1/assets`), Then HTTP 401(Invalid token) 반환.
|
||||
3. Given daemon에 바인딩된 token이 0개인 daemon, When `--deactivate --force` 실행, Then daemon만 비활성화되고 token revoke 동작은 no-op이며 정상 종료.
|
||||
4. Given AC-1 실행 후 `--activate`로 재활성화, When 재실행, Then daemon만 활성화되고 이미 revoke된 token은 자동 복구되지 않는다(운영자가 신규 발급 필요).
|
||||
5. Given deactivation 실행 중 in-flight 거래(daemon이 ledger trade 호출 중), When 자동 revoke가 즉시 적용, Then in-flight 거래는 daemon 측 graceful drain 책임이며 본 REQ는 추가 보호하지 않음(release plan SOP 참조).
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/runtime-flows.md` — "Daemon Deactivation 자동 Revoke" flow 추가 (REQ-019 baseline 작업에 포함)
|
||||
- `README.md` — daemon provisioning runbook의 deactivation SOP 갱신 (REQ-019 또는 release plan)
|
||||
- Modules:
|
||||
- `app/cli/commands/daemon.py:update_daemon` — `--deactivate` 분기에서 자동 revoke 호출 추가
|
||||
- `app/services/auth_service.py` 또는 신규 service — `revoke_tokens_for_daemon(daemon_id)` 헬퍼 추가
|
||||
- `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py` — `revoke_tokens_by_daemon_id(daemon_id)` 메서드 신규 추가
|
||||
- Tests:
|
||||
- 갱신 `tests/test_daemon_registry.py` — AC-1~4 회귀 시나리오 추가
|
||||
- 신규 또는 갱신 `tests/test_v1_*.py` — AC-2 revoke된 token으로 v1 호출 시 401 확인
|
||||
|
||||
## Notes
|
||||
- graceful drain을 본 REQ 범위에 포함하지 않기로 결정(dcy-004 OQ-3). drain은 daemon 측(trex-basic 등)이 종료 신호 수신 시 in-flight 마무리 후 종료하는 형태로 구현되어야 한다. portfolio-hub 측은 즉시 revoke만 수행.
|
||||
- 본 REQ는 daemon registry CRUD(REQ-009/010 후속의 daemon 모델) 위에 얹는 추가 동작이며 daemon 모델 자체에는 변경 없음.
|
||||
- 재활성화 시 token 자동 복구를 하지 않는 이유: revoke된 token은 보안상 폐기 의미가 있어 자동 복구하면 권한 escalation 위험. 운영자가 신규 발급 절차를 통해 새 token을 부여하는 게 안전.
|
||||
- Source: dcy-004 FR-7, dcy-004 OQ-3 결정.
|
||||
|
||||
## 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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-7 산출물.
|
||||
|
||||
#### Intent
|
||||
- daemon deactivation 운영 공백 해소 — 비활성 daemon의 token이 계속 유효한 문제를 즉시 revoke 정책으로 정상화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/runtime-flows.md, README.md — 최초 지정 (REQ-019 baseline 작업 또는 release plan)
|
||||
- Modules: app/cli/commands/daemon.py, app/services/auth_service.py, app/repositories/portfolio_repository.py, app/repositories/postgres.py — 최초 지정
|
||||
- Tests: tests/test_daemon_registry.py, tests/test_v1_*.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 계획 편입
|
||||
|
||||
#### Validation Plan
|
||||
- pytest로 AC-1~4 검증; AC-5는 정책 reference (release plan SOP 점검)
|
||||
@@ -0,0 +1,103 @@
|
||||
# REQ-023: NULL daemon_id legacy token 마이그레이션 CLI
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Interface
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- `daemon_id IS NULL`인 active service token이 운영 환경에 존재하면 v1 API daemon-scoped endpoints가 fail-closed로 작동해 운영 정리가 필요하다. 자동 매핑은 권한 escalation 리스크로 채택하지 않고(dcy-004 OQ-7), 운영자 수동 매핑을 보조하는 진단 CLI를 제공한다.
|
||||
- dry-run으로 inventory + 매핑 후보를 진단 출력하고, 운영자가 token별 `rebind` 또는 `revoke`를 명시적으로 실행하는 패턴을 표준화한다.
|
||||
|
||||
## Requirement
|
||||
- 신규 CLI 서브커맨드 `pfhub service-token migrate-null-daemon` 추가:
|
||||
- `--dry-run` 옵션: NULL daemon active service token inventory를 출력. 각 token에 대해 `id`, `user_id`, `name`, `runtime_id`, `created_at`, 매핑 후보 daemon(같은 `owner_user_id == user_id`인 active daemon 목록)을 표 또는 JSON 형식으로 보고.
|
||||
- 매핑 후보 검증: `token.user_id == daemon.owner_user_id` 일치 여부를 명시 표시. 불일치 가능성이 있는 token은 별도 경고 표시.
|
||||
- `--dry-run` 없이 실행하면 안전상 명시적 거부(예: "use --dry-run for inspection, then `pfhub service-token rebind` or `revoke` per token") 또는 no-op.
|
||||
- 본 CLI는 자동 rebind/revoke를 수행하지 않는다. 실제 적용은 운영자가 token별로 `pfhub service-token rebind NAME --daemon-id ID` 또는 `pfhub service-token revoke NAME`을 명시 실행해야 한다.
|
||||
- 본 CLI는 idempotent하다(재실행 안전, NULL token이 0건이어도 정상 종료).
|
||||
- DATA-2 inventory(2026-05-20)에서 발견된 admin 테스트 fixture 1건은 본 batch release 시점에 운영자가 직접 `pfhub service-token revoke admin` 수동 실행으로 정리(release plan에 명시).
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `pfhub service-token migrate-null-daemon --dry-run` 실행, When NULL daemon active service token 1건 이상 존재, Then 각 token의 `id`, `user_id`, `name`, `runtime_id`, `created_at`, 매핑 후보 daemon 목록, user_id 일치 여부가 출력된다.
|
||||
2. Given `pfhub service-token migrate-null-daemon --dry-run` 실행, When NULL daemon 활성 service token이 0건, Then "no NULL daemon tokens" 메시지 출력 후 exit code 0으로 정상 종료.
|
||||
3. Given `pfhub service-token migrate-null-daemon` (--dry-run 없이) 실행, When 호출, Then 자동 변경을 거부하고(예: error message + non-zero exit) 운영자에게 `rebind`/`revoke` 수동 실행을 안내한다.
|
||||
4. Given AC-1 출력 후 운영자가 `pfhub service-token rebind NAME --daemon-id ID` 실행, When 매핑이 성공적으로 적용, Then 해당 token의 `daemon_id`가 갱신되고 다시 `--dry-run` 실행 시 해당 token이 inventory에서 제외된다.
|
||||
5. Given AC-1 실행 후 운영자가 `pfhub service-token revoke admin` 실행, When DATA-2 admin 테스트 fixture가 revoke, Then 다시 `--dry-run` 실행 시 해당 token이 inventory에서 제외된다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — `pfhub service-token migrate-null-daemon` CLI 명세 추가 (REQ-019 baseline 작업)
|
||||
- Modules:
|
||||
- `app/cli/commands/service_tokens.py` — `migrate_null_daemon` 함수 신규 추가 (Typer 서브커맨드 등록)
|
||||
- `app/services/auth_service.py` — `list_null_daemon_service_tokens()` 및 `find_candidate_daemons_for_user(user_id)` 헬퍼 추가(또는 직접 repository 조회)
|
||||
- `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py` — `list_service_tokens_with_null_daemon()` 메서드 신규 추가
|
||||
- Tests:
|
||||
- 신규 `tests/test_null_daemon_migration.py` — AC-1~5 시나리오. dry-run 출력 검증 + idempotency 회귀 + non-dry-run 거부 동작 검증
|
||||
|
||||
## Notes
|
||||
- dcy-004 OQ-7 결정으로 자동 매핑/auto-create daemon은 채택하지 않음(권한 escalation 리스크). 본 CLI는 진단 도구로만 작동.
|
||||
- DATA-2 inventory 1건(admin 테스트 fixture)은 본 batch release 시점에 운영자가 직접 처리. CLI 진단 + 운영자 수동 적용이라는 본 REQ 패턴의 첫 번째 적용 사례.
|
||||
- 매핑 후보가 0건(같은 user_id의 active daemon 없음)인 token은 dry-run 출력에 별도 표시해 운영자가 daemon 신규 생성 후 rebind를 진행하거나 token revoke를 결정하도록 안내.
|
||||
- 본 CLI는 batch 시점 1회 정리 + 향후 NULL daemon token이 발생할 때마다 사용 가능한 영구 운영 도구로 작동한다.
|
||||
- Source: dcy-004 FR-8, dcy-004 NFR-5, dcy-004 OQ-7 결정.
|
||||
|
||||
## 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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-8 산출물.
|
||||
|
||||
#### Intent
|
||||
- NULL daemon legacy token 마이그레이션을 운영자 수동 매핑 패턴으로 표준화 + 진단 CLI 제공.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md (REQ-019에서 처리)
|
||||
- Modules: app/cli/commands/service_tokens.py, app/services/auth_service.py, app/repositories/portfolio_repository.py, app/repositories/postgres.py — 최초 지정
|
||||
- Tests: tests/test_null_daemon_migration.py — 최초 지정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정). DATA-2 정리(admin 테스트 fixture revoke)는 release plan에 명시.
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입
|
||||
|
||||
#### Validation Plan
|
||||
- pytest로 AC-1~5 검증; CLI smoke + idempotency 회귀 + non-dry-run 거부 동작
|
||||
@@ -0,0 +1,113 @@
|
||||
# REQ-025: POST /v1/portfolios 신설 (토큰 분기 + catalog 검증)
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- v1 API에 portfolio 생성 경로(`POST /v1/portfolios`)를 신설해 user/admin이 HTTP로 본인 또는 타 user의 portfolio를 직접 프로비저닝할 수 있도록 한다.
|
||||
- dcy-001 FR-4의 catalog 검증(asset_id active)을 portfolio 생성 시점에 통합 적용해 dcy-001 asset catalog 모델 위에서만 portfolio가 생성되도록 강제한다.
|
||||
|
||||
## Requirement
|
||||
- 신규 라우트 `POST /v1/portfolios` (Bearer 인증 필수).
|
||||
- **토큰 분기 규칙** (dcy-003 OQ-2 + OQ-5 결정, dcy-004 정책과 정합):
|
||||
- `token_type ∈ {service, client}` → HTTP 403 fail-closed (`detail="service token cannot create portfolios"` 또는 client 대응 메시지). service는 daemon-bound로 portfolio CRUD 주체가 아니며 client는 dcy-004 REQ-016에서 폐지 확정.
|
||||
- 그 외(`token_type=session` 또는 env token):
|
||||
- `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.
|
||||
- **catalog 검증** (dcy-001 FR-4 통합): `asset_id`가 `assets` 테이블에 존재하고 `status='active'`인지 검증. 미등록/inactive면 dcy-001 FR-4와 일관된 응답 코드로 거부(HTTP 422 또는 403; REQ 단계 확정은 본 REQ 또는 후속 보강).
|
||||
- **body 스키마** (`PortfolioCreateRequest` 신규):
|
||||
- 필수: `asset_id`, `broker`
|
||||
- 선택: `name`, `cash_balance`, `asset_quantity`, `asset_price`, `profit_buffer`, `strategy_name`, `strategy_params`, `managed_by_daemon_id`, `user_id`(admin 분기 전용)
|
||||
- 응답: HTTP 201 + 생성된 portfolio의 `PortfolioItem`(`portfolio_id`, `asset_id`, `owner_user_id`, ...).
|
||||
- catalog 검증 위치(서비스 계층 공통 진입점 vs v1 라우터 레이어 한정)는 본 REQ 구현 시점에 결정. dcy-003 R-5 트레이드오프 reference 참조.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given admin token + body `{"asset_id": "<catalog-uuid>", "broker": "kiwoom", "user_id": "<target-user-uuid>"}`, When `POST /v1/portfolios`, Then HTTP 201 + 생성된 portfolio의 `owner_user_id == <target-user-uuid>` 반환.
|
||||
2. Given session/env token with `permission ∈ {read, trade-write}` + body `{"asset_id": ..., "broker": ...}`, When `POST /v1/portfolios`, Then HTTP 201 + `owner_user_id == auth.user_id` 반환.
|
||||
3. Given service token, When `POST /v1/portfolios`, Then HTTP 403 fail-closed + `detail="service token cannot create portfolios"`.
|
||||
4. Given client token(폐지 전 운영 단계), When `POST /v1/portfolios`, Then HTTP 403 fail-closed.
|
||||
5. Given body `asset_id`가 catalog에 미존재 또는 `status='inactive'`, When `POST /v1/portfolios`, Then HTTP 422 또는 403로 거부되고 portfolio row가 생성되지 않는다.
|
||||
6. Given Bearer token 없음, When `POST /v1/portfolios`, Then HTTP 401.
|
||||
7. Given non-admin token + body `user_id` 지정, When `POST /v1/portfolios`, Then `user_id`는 무시되어 `owner_user_id == auth.user_id`로 생성되거나 HTTP 422.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — POST /v1/portfolios 명세 추가 (REQ-032 baseline 작업)
|
||||
- `docs/runtime-flows.md` — portfolio create flow + catalog 검증 단계 (REQ-032)
|
||||
- Modules:
|
||||
- `app/api/v1_portfolios.py` (신규) — POST /v1/portfolios 라우트, 토큰 분기 인증 헬퍼(`_require_portfolio_access`)
|
||||
- `app/main.py` — v1_portfolios_router include
|
||||
- `app/schemas/v1.py` — `PortfolioCreateRequest`, `PortfolioItem` 신규
|
||||
- `app/services/portfolio_service.py:create_portfolio` — catalog 검증 통합(dcy-001 FR-4 정책)
|
||||
- `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py` — `get_asset_by_id(asset_id, only_active=True)` 헬퍼(또는 직접 query) 추가
|
||||
- Tests:
|
||||
- 신규 `tests/test_v1_portfolios_crud.py` — AC-1~7 검증
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 dcy-003 FR-1(생성) + FR-2(catalog 검증)을 통합. 생성 시점에 catalog 검증이 자연 적용되므로 단일 REQ로 분리.
|
||||
- catalog 검증 위치: dcy-003 §5 FR-2 본문에 "REQ 작성 단계에서 결정" 위임. R-5(CLI 회귀) 완화 방안을 참조해 본 REQ 구현 시 결정.
|
||||
- 토큰 분기 게이트는 dcy-004 REQ-016 client 폐지 이후 client 분기가 자연 사라지나, 폐지 release 이전·이후 모두 정합하도록 게이트 자체는 유지(env token 침투 방어).
|
||||
- daemon이 portfolio를 직접 생성하지 못함(OQ-2). v1 API에서 daemon의 권한은 조회(REQ-028)와 ledger 기록(REQ-029)에 한정.
|
||||
- Source: dcy-003 FR-1, FR-2, OQ-2, OQ-5, NFR-1, NFR-3, NFR-4. dcy-001 FR-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-1 + FR-2 통합 산출물.
|
||||
|
||||
#### Intent
|
||||
- v1 portfolio mutation의 핵심 진입점(생성) + dcy-001 catalog 검증 통합.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~7 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md, docs/runtime-flows.md (REQ-032에서 처리)
|
||||
- Modules: app/api/v1_portfolios.py (신규), app/main.py, app/schemas/v1.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 계획 편입; dcy-001 REQ-009/011/012 선행 의존성 확인(assets 테이블 + portfolios asset_id FK)
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~7 검증; 토큰 타입별·permission별 시나리오 + catalog hit/miss
|
||||
@@ -0,0 +1,110 @@
|
||||
# REQ-026: PATCH /v1/portfolios/{portfolio_id} 신설
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- v1 API에 portfolio 부분 수정 경로(`PATCH /v1/portfolios/{portfolio_id}`)를 신설해 user/admin이 HTTP로 portfolio 속성(broker/status/name/symbol/strategy 등)을 직접 수정할 수 있도록 한다.
|
||||
- 토큰 분기 + ownership 검증을 REQ-025와 동일 패턴으로 적용해 v1 portfolio CRUD 인가 일관성을 확보한다.
|
||||
|
||||
## Requirement
|
||||
- 신규 라우트 `PATCH /v1/portfolios/{portfolio_id}` (Bearer 인증 필수).
|
||||
- **토큰 분기 규칙** (REQ-025 + dcy-003 OQ-2/OQ-5 결정과 정합):
|
||||
- `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.
|
||||
- 비존재 portfolio는 HTTP 404.
|
||||
- 수정 가능 필드는 기존 `UpdatePortfolioRequest` 범위: `broker`, `status`, `name`, `symbol`, `strategy_name`, `strategy_params`, `asset_price`, `managed_by_daemon_id` 등.
|
||||
- **disabled portfolio 보호** (REQ-027 soft delete 정책 정합): `portfolios.status == 'disabled'`인 portfolio에 대해 status 전이(`'enabled'`로 복구) 외 다른 필드 변경 시 HTTP 409 또는 422로 거부.
|
||||
- 응답: HTTP 200 + 갱신된 `PortfolioItem`.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given admin token + 본인 또는 타 user portfolio, When `PATCH /v1/portfolios/{id}` body `{"strategy_name": "..."}`, Then HTTP 200 + 갱신된 portfolio 반환.
|
||||
2. Given session/env token with `permission ∈ {read, trade-write}` + 본인 portfolio, When `PATCH /v1/portfolios/{id}`, Then HTTP 200 + 갱신된 portfolio.
|
||||
3. Given session/env token with `permission ∈ {read, trade-write}` + 타 user portfolio, When `PATCH /v1/portfolios/{id}`, Then HTTP 403 (ownership 불일치).
|
||||
4. Given service token, When `PATCH /v1/portfolios/{id}`, Then HTTP 403 fail-closed.
|
||||
5. Given client token(폐지 전), When `PATCH /v1/portfolios/{id}`, Then HTTP 403 fail-closed.
|
||||
6. Given Bearer token 없음, When `PATCH /v1/portfolios/{id}`, Then HTTP 401.
|
||||
7. Given 비존재 `portfolio_id`, When `PATCH /v1/portfolios/{id}`, Then HTTP 404.
|
||||
8. Given `status='disabled'` portfolio + body `{"strategy_name": "..."}`(status 외 필드), When `PATCH`, Then HTTP 409 또는 422 (disabled write 차단).
|
||||
9. Given `status='disabled'` portfolio + body `{"status": "enabled"}` (admin), When `PATCH`, Then HTTP 200 + status 복구.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — PATCH /v1/portfolios/{id} 명세 추가 (REQ-032)
|
||||
- `docs/runtime-flows.md` — portfolio update flow + ownership 검증 + disabled 보호 (REQ-032)
|
||||
- Modules:
|
||||
- `app/api/v1_portfolios.py` — PATCH 라우트 추가 (REQ-025의 인증 헬퍼 재사용)
|
||||
- `app/schemas/v1.py` — `PortfolioUpdateRequest` 신규 (기존 `UpdatePortfolioRequest` 재사용 또는 v1 전용 래핑)
|
||||
- `app/services/portfolio_service.py:update_portfolio` — 기존 메서드 재사용 + disabled 보호 검증 추가
|
||||
- Tests:
|
||||
- 신규/갱신 `tests/test_v1_portfolios_crud.py` — AC-1~9 검증
|
||||
|
||||
## Notes
|
||||
- ownership 검증은 admin/user 분기 후 service 계층 또는 라우터에서 일관 처리. 본 REQ의 검증 위치는 REQ-025 catalog 검증 위치 결정과 함께 결정한다.
|
||||
- disabled portfolio 보호는 REQ-027(soft delete) 정책의 일부로, 본 REQ에서 PATCH 측면 검증을 명시. ledger trade/tag/comment 측면은 REQ-029/030/031에서 각각 처리.
|
||||
- 본 REQ는 dcy-003 FR-3 산출물이며 NFR-1/NFR-3/NFR-4를 검증 기준에 흡수.
|
||||
- Source: dcy-003 FR-3, 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-3 산출물.
|
||||
|
||||
#### Intent
|
||||
- v1 portfolio mutation의 부분 수정 진입점 + ownership 검증 + disabled 보호.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~9 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md, docs/runtime-flows.md (REQ-032)
|
||||
- Modules: app/api/v1_portfolios.py, app/schemas/v1.py, app/services/portfolio_service.py — 최초 지정
|
||||
- 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와 동일 batch 권장
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~9 검증; ownership·token type·disabled 보호 시나리오
|
||||
@@ -0,0 +1,121 @@
|
||||
# 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 차단 일관성
|
||||
@@ -0,0 +1,116 @@
|
||||
# REQ-028: GET /v1/portfolios 신설 (토큰 분기 조회)
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- v1 API에 portfolio 조회 경로(`GET /v1/portfolios`)를 신설해 service/user/admin 세 principal이 단일 엔드포인트로 분기 조회할 수 있도록 한다.
|
||||
- dcy-001 FR-5(REQ-013 Deprecated 처리)에서 회수된 `GET /v1/assets` 통합 endpoint 정의를 본 신규 path로 이전한다(dcy-003 옵션 B 결정).
|
||||
|
||||
## Requirement
|
||||
- 신규 라우트 `GET /v1/portfolios` (Bearer 인증 필수).
|
||||
- **토큰 분기 규칙** (dcy-003 OQ-5 결정):
|
||||
- `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 REQ-016 폐지 확정 정합).
|
||||
- `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만 반환.
|
||||
- **`?market=<code>` 필터**: 모든 토큰 타입에서 지원. `<code> = ANY(assets.markets)`인 portfolio만 반환(catalog와 join).
|
||||
- **`?include_disabled=true` 옵션** (dcy-003 R-2 완화): 기본 응답은 `status='disabled'`인 portfolio 제외. opt-in 시에만 포함.
|
||||
- 응답 항목 schema: 신규 `PortfolioItem` (`portfolio_id`, `asset_id`, `owner_user_id`, `broker`, `status`, `cash_balance`, ..., `tag_ids`, `managed_by_daemon_id`, ...). `asset_id`는 catalog의 `assets.asset_id`(UUID) 참조.
|
||||
- 응답: HTTP 200 + `PortfolioListResponse` (`portfolios: list[PortfolioItem]`, `total: int`).
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given service token(daemon-bound, `daemon_id` 있음), When `GET /v1/portfolios`, Then `managed_by_daemon_id = auth.daemon_id`인 portfolio 목록만 반환.
|
||||
2. Given service token(`daemon_id` 없음), When `GET /v1/portfolios`, Then HTTP 403 fail-closed.
|
||||
3. Given session/env token + `permission ∈ {read, trade-write}`, When `GET /v1/portfolios`, Then `owner_user_id = auth.user_id`인 portfolio 목록만 반환.
|
||||
4. Given admin token (no `?user_id`), When `GET /v1/portfolios`, Then admin 본인의 portfolio 목록 반환.
|
||||
5. Given admin token + `?user_id=<target>`, When `GET /v1/portfolios`, Then `<target>` user의 portfolio 목록 반환.
|
||||
6. Given 임의 토큰 + `?market=KRX`, When `GET /v1/portfolios?market=KRX`, Then `'KRX' = ANY(assets.markets)`인 portfolio만 반환.
|
||||
7. Given disabled portfolio 1건 + active portfolio 1건 보유 user, When `GET /v1/portfolios` (`?include_disabled` 없음), Then active 1건만 반환.
|
||||
8. Given AC-7 환경 + `?include_disabled=true`, When `GET /v1/portfolios?include_disabled=true`, Then 2건 모두 반환.
|
||||
9. Given client token, When `GET /v1/portfolios`, Then HTTP 403 fail-closed.
|
||||
10. Given Bearer token 없음, When `GET /v1/portfolios`, Then HTTP 401.
|
||||
11. Given 응답 portfolio 항목, When `asset_id` 필드 확인, Then `assets.asset_id`(catalog UUID)를 참조한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — GET /v1/portfolios 명세 + `?market`/`?user_id`/`?include_disabled` 파라미터 (REQ-032)
|
||||
- `docs/runtime-flows.md` — 토큰 분기 조회 flow + daemon-scoped 필터 (REQ-032)
|
||||
- Modules:
|
||||
- `app/api/v1_portfolios.py` — GET 라우트 (REQ-025의 인증 헬퍼 재사용 + GET 전용 service 토큰 분기 추가)
|
||||
- `app/api/v1_assets.py` — 기존 `list_assets` 함수에서 portfolio 조회 로직 제거(catalog 전용 회수). REQ-013 Deprecated 후속 정리.
|
||||
- `app/schemas/v1.py` — `PortfolioItem`, `PortfolioListResponse` 신규
|
||||
- `app/services/portfolio_service.py:list_portfolios` — token-type별 분기 + `?market`/`?include_disabled` 처리. dcy-001 REQ-013 Deprecated 후속 작업 흡수.
|
||||
- `app/repositories/postgres.py:list_portfolios_with_strategies` — `managed_by_daemon_id` 필터 + `markets` array 필터 + `include_disabled` 분기
|
||||
- Tests:
|
||||
- 신규 `tests/test_v1_portfolios_list.py` — AC-1~11 검증
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 dcy-001 REQ-013 Deprecated(CHG-20260520-01)의 후속 신규 REQ. 토큰 분기 정책은 REQ-013의 본문을 path만 변경해 흡수.
|
||||
- `GET /v1/assets`(catalog 조회 의미)는 본 REQ 범위 밖이며 dcy-001 batch design 단계에서 REQ-010 보강 또는 신규 REQ로 처리.
|
||||
- service token이 NULL `daemon_id`로 호출하면 fail-closed. NULL daemon legacy token 운영 정리는 dcy-004 REQ-023(`migrate-null-daemon` CLI)로 처리.
|
||||
- `?include_disabled` 기본 false 정책은 R-2 완화의 핵심. REQ-027 soft delete와 정합.
|
||||
- Source: dcy-003 FR-5, OQ-5, NFR-1, NFR-3, R-2 완화. dcy-001 REQ-013 후속.
|
||||
|
||||
## 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-5 산출물 + REQ-013 Deprecated 후속.
|
||||
|
||||
#### Intent
|
||||
- v1 portfolio 조회 신설 + 토큰 분기 정책 path 이전 + R-2 disabled 가시성 완화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~11 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md, docs/runtime-flows.md (REQ-032)
|
||||
- Modules: app/api/v1_portfolios.py, app/api/v1_assets.py(catalog 회수), app/schemas/v1.py, app/services/portfolio_service.py, app/repositories/postgres.py — 최초 지정
|
||||
- Tests: tests/test_v1_portfolios_list.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 계획 편입; dcy-001 REQ-013 Deprecated 정리 후속
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~11 검증; 토큰 분기 + `?market` + `?user_id` + `?include_disabled` 시나리오
|
||||
@@ -0,0 +1,112 @@
|
||||
# REQ-029: POST /v1/portfolios/{portfolio_id}/ledger/trades 이전
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 기존 `POST /v1/assets/{asset_id}/ledger/trades`(`app/api/v1_ledger.py`)의 path param 이름이 `asset_id`이나 실제 값은 `portfolios.portfolio_id`(UUID)로 의미 혼용되는 결함을 해소한다.
|
||||
- dcy-003 옵션 B(path 명명 분리)에 따라 신 path `POST /v1/portfolios/{portfolio_id}/ledger/trades`로 이전하고 구 path는 동일 release에서 제거한다. 동작·인가·idempotency는 그대로 보존.
|
||||
|
||||
## Requirement
|
||||
- 신규 라우트 `POST /v1/portfolios/{portfolio_id}/ledger/trades` (Bearer 인증 필수).
|
||||
- **동작은 기존 `POST /v1/assets/{asset_id}/ledger/trades`와 동일하게 보존**:
|
||||
- **daemon assignment 3단계 fail-closed 인가**: (1) token `daemon_id IS NULL` → HTTP 403, (2) portfolio `managed_by_daemon_id IS NULL`(미배정 asset) → HTTP 403, (3) `portfolio.managed_by_daemon_id != auth.daemon_id`(불일치) → HTTP 403.
|
||||
- **idempotency**: `(portfolio_id, order_id, execution_id)` unique constraint (기존 `uq_ledger_trades_idempotency` 재사용).
|
||||
- **duplicate/conflict 응답**: 동일 키 + 동일 payload → HTTP 200 + `status="duplicate"`. 동일 키 + 다른 payload → HTTP 409.
|
||||
- **새 ledger row** → HTTP 201 + `status="new"`.
|
||||
- **disabled portfolio 차단** (REQ-027 정합): `portfolios.status='disabled'`인 portfolio에 대해서는 HTTP 409 또는 422로 차단.
|
||||
- 구 path `POST /v1/assets/{asset_id}/ledger/trades` 제거: 동일 batch release에서 라우트 삭제. 호출 시 HTTP 404.
|
||||
- **trex-basic 동시 배포 필수** (dcy-003 OQ-3 + NFR-2): 신 path 단일 서빙. 한시적 별칭/redirect 없음. release plan에 배포 순서 잠금(trex-basic 클라이언트 사전 빌드 → portfolio-hub 배포 → trex-basic 배포) + rollback 시 trex-basic 동반 rollback 전제 명시.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given service token + 본인 daemon에 배정된 active portfolio + 신규 trade payload, When `POST /v1/portfolios/{portfolio_id}/ledger/trades`, Then HTTP 201 + `status="new"` + 생성된 ledger row.
|
||||
2. Given AC-1 실행 후 동일 key + 동일 payload 재호출, When 호출, Then HTTP 200 + `status="duplicate"` + 단일 ledger row 유지.
|
||||
3. Given AC-1 실행 후 동일 key + 다른 payload 호출, When 호출, Then HTTP 409 + ledger row 변경 없음.
|
||||
4. Given service token `daemon_id IS NULL`, When `POST /v1/portfolios/{portfolio_id}/ledger/trades`, Then HTTP 403 fail-closed.
|
||||
5. Given service token + `portfolio.managed_by_daemon_id IS NULL`(미배정 asset), When 호출, Then HTTP 403 fail-closed.
|
||||
6. Given service token + `portfolio.managed_by_daemon_id != auth.daemon_id`(불일치), When 호출, Then HTTP 403 fail-closed.
|
||||
7. Given service token + `status='disabled'` portfolio, When 호출, Then HTTP 409 또는 422 (disabled write 차단).
|
||||
8. Given 비존재 `portfolio_id`, When 호출, Then HTTP 404.
|
||||
9. Given 구 path `POST /v1/assets/{asset_id}/ledger/trades` 호출, When 본 REQ 구현 후 동일 batch release, Then HTTP 404.
|
||||
10. Given Bearer token 없음, When 호출, Then HTTP 401.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — POST /v1/portfolios/{id}/ledger/trades 명세 + 구 path 제거 (REQ-032)
|
||||
- `docs/runtime-flows.md` — daemon assignment 3단계 인가 + idempotency + path 이전 흐름 (REQ-032)
|
||||
- Modules:
|
||||
- `app/api/v1_portfolios.py` — `POST /v1/portfolios/{portfolio_id}/ledger/trades` 라우트 추가 (또는 별도 `v1_ledger.py`를 신 path로 이전)
|
||||
- `app/api/v1_ledger.py` — 구 path 제거 또는 신 path로 라우트 prefix 이전
|
||||
- `app/main.py` — 라우터 include 갱신 (구 ledger 라우터 제거)
|
||||
- `app/services/portfolio_service.py` 또는 `app/services/ledger_service.py` — 기존 ledger trade 로직 재사용 + disabled 차단 검증 추가
|
||||
- Tests:
|
||||
- 갱신 `tests/test_v1.py` — 구 path 케이스를 신 path로 갱신, 구 path 호출 시 HTTP 404 확인
|
||||
- 신규 `tests/test_v1_portfolios_ledger.py` — AC-1~10 검증
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 기능 추가가 아닌 **path 이전**이라 기존 동작·테스트 시나리오를 그대로 재사용한다. 다만 disabled portfolio 차단(AC-7)은 REQ-027 soft delete 정책 정합 검증으로 신규.
|
||||
- dcy-003 NFR-2(trex-basic 동시 배포)는 본 REQ의 가장 큰 운영 리스크(R-1)이며, release plan에 배포 순서 잠금·rollback 전제를 명시해야 함. release plan은 batch design/release 단계 작업.
|
||||
- ledger_trades 테이블의 `asset_id` 컬럼명은 본 REQ에서 변경하지 않는다(dcy-001 `assets.asset_id`와 의미 혼용이 잔존하나, ledger_trades 컬럼은 portfolio_id를 가리킨다는 점이 path 이름 정합화로 충분히 명확해짐). 컬럼명 변경은 별도 migration REQ로 분리 가능.
|
||||
- Source: dcy-003 FR-6, OQ-3, NFR-1, NFR-2, R-1 완화.
|
||||
|
||||
## 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-6 산출물.
|
||||
|
||||
#### Intent
|
||||
- ledger trade path 명명 정합화 + 동시 배포 정책 명시 + disabled 차단 추가.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~10 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md, docs/runtime-flows.md (REQ-032)
|
||||
- Modules: app/api/v1_portfolios.py, app/api/v1_ledger.py(제거 또는 이전), app/main.py, app/services/* — 최초 지정
|
||||
- Tests: tests/test_v1.py(갱신), tests/test_v1_portfolios_ledger.py(신규) — 최초 지정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정). trex-basic 동시 배포 release plan은 batch design 단계에서 작성.
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입; trex-basic 측 release readiness(DATA_TREX_BASIC_RELEASE_WINDOW) 외부 확인 필요
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~10 검증; 신·구 path 회귀 + daemon 인가 3단계 + idempotency·conflict + disabled 차단
|
||||
@@ -0,0 +1,116 @@
|
||||
# REQ-030: portfolio tag 적용/해제/조회 v1 라우트
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- v1 API에 portfolio tag 적용·해제·조회 경로(`/v1/portfolios/{portfolio_id}/tags`)를 신설해 user/admin이 HTTP로 portfolio에 태그를 적용·관리할 수 있도록 한다.
|
||||
- dcy-002 FR-3 portfolios.tag_ids 정책(`tags.is_admin_tag=TRUE` 또는 `tags.created_by_user_id = portfolio.owner_user_id`)을 v1 라우트에 일관 적용해 태그 적용 정책 위반을 fail-closed로 거부한다.
|
||||
|
||||
## Requirement
|
||||
- 신규 라우트 (Bearer 인증 필수):
|
||||
- `POST /v1/portfolios/{portfolio_id}/tags` — body: `{"tag_id": "<uuid>"}`. 응답 HTTP 201 + 적용된 tag 정보.
|
||||
- `DELETE /v1/portfolios/{portfolio_id}/tags/{tag_id}` — 응답 HTTP 200 또는 204.
|
||||
- `GET /v1/portfolios/{portfolio_id}/tags` — 응답 HTTP 200 + 해당 portfolio에 적용된 tag 목록.
|
||||
- **ownership 규칙** (REQ-026과 동일):
|
||||
- `token_type ∈ {service, client}` → HTTP 403 fail-closed.
|
||||
- `permission='admin'` → 모든 portfolio.
|
||||
- `permission ∈ {read, trade-write}` → 자기 소유 portfolio만, 불일치 시 HTTP 403.
|
||||
- **dcy-002 FR-3 태그 적용 정책 검증** (POST 시): `tags.is_active=TRUE`이고 (`tags.is_admin_tag=TRUE` 또는 `tags.created_by_user_id = portfolio.owner_user_id`)인 경우에만 적용 허용. 정책 위반 시 HTTP 403.
|
||||
- **disabled portfolio 차단** (REQ-027 정합): `status='disabled'`인 portfolio에 대해 POST/DELETE는 HTTP 409 또는 422로 거부. GET은 허용(`?include_disabled=true` 별도 제어 불필요 — GET tag는 항상 조회 가능).
|
||||
- 비존재 `portfolio_id` 또는 `tag_id`는 HTTP 404.
|
||||
- tag 적용 후 portfolio의 `tag_ids` UUID 배열에 추가(dcy-002 portfolios.tag_ids 스키마 반영). 중복 적용은 HTTP 409 또는 멱등 200 (REQ 구현 시 확정).
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given session token + 본인 active portfolio + admin이 생성한 active tag, When `POST /v1/portfolios/{id}/tags` body `{"tag_id": ...}`, Then HTTP 201 + portfolio.tag_ids 배열에 tag_id 추가.
|
||||
2. Given session token + 본인 portfolio + 본인이 생성한 active tag, When `POST`, Then HTTP 201.
|
||||
3. Given session token + 본인 portfolio + 타 user 생성한 non-admin tag, When `POST`, Then HTTP 403 (dcy-002 FR-3 정책 위반).
|
||||
4. Given session token + 본인 portfolio + `tags.is_active=FALSE` tag, When `POST`, Then HTTP 403 (비활성 tag 적용 불가).
|
||||
5. Given admin token + 타 user portfolio + 임의 active tag, When `POST`, Then dcy-002 정책 그대로 적용(admin tag 또는 portfolio.owner 생성 tag만 허용). 위반 시 HTTP 403.
|
||||
6. Given session token + 타 user portfolio, When `POST`, Then HTTP 403 (ownership 불일치).
|
||||
7. Given service/client token, When `POST`, Then HTTP 403 fail-closed.
|
||||
8. Given session token + disabled portfolio, When `POST` 또는 `DELETE`, Then HTTP 409 또는 422.
|
||||
9. Given session token + 본인 portfolio + 적용된 tag, When `DELETE /v1/portfolios/{id}/tags/{tag_id}`, Then HTTP 200/204 + portfolio.tag_ids 배열에서 제거.
|
||||
10. Given session token + 본인 portfolio, When `GET /v1/portfolios/{id}/tags`, Then HTTP 200 + 해당 portfolio에 적용된 tag 목록 반환.
|
||||
11. Given 비존재 `portfolio_id` 또는 `tag_id`, When 호출, Then HTTP 404.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — `/v1/portfolios/{id}/tags` 명세 (REQ-032)
|
||||
- Modules:
|
||||
- `app/api/v1_portfolios.py` — tag 서브 라우트 추가
|
||||
- `app/schemas/v1.py` — `PortfolioTagApplyRequest`, `PortfolioTagResponse` 신규
|
||||
- `app/services/portfolio_service.py:add_tag`/`remove_tag`/`list_tags` — dcy-002 FR-3 정책 검증 추가
|
||||
- `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py` — `apply_tag(portfolio_id, tag_id)`, `remove_tag`, `list_tags` 메서드 (dcy-002 REQ-003 portfolios.tag_ids 배열 처리와 정합)
|
||||
- Tests:
|
||||
- 신규 `tests/test_v1_portfolios_tags_comments.py` — AC-1~11 (tag 부분)
|
||||
|
||||
## Notes
|
||||
- dcy-002 REQ-001(tags 마스터)·REQ-003(portfolios.tag_ids) 선행 필요. dcy-002 REQ가 모두 Approved 상태이므로 batch design 단계에서 의존성 순서 명시.
|
||||
- 본 REQ의 tag 적용 정책 검증은 dcy-002 FR-3 본문을 그대로 채택. dcy-002 REQ-005(태그 적용·해제 HTTP API)와 본 REQ의 범위 경계는 다음과 같이 분리:
|
||||
- dcy-002 REQ-005: `/v1/portfolios/{id}/tags` 적용 정책 정의 + 일반 portfolio 측면
|
||||
- 본 REQ: v1 portfolio mutation surface 내에서의 라우트·ownership·disabled 통합 검증
|
||||
- 본 REQ가 dcy-002 REQ-005를 흡수하거나 보강하는 형태로 batch design 단계에서 결정.
|
||||
- Source: dcy-003 FR-7, dcy-002 FR-3.
|
||||
|
||||
## 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-7 산출물.
|
||||
|
||||
#### Intent
|
||||
- v1 portfolio tag 적용/해제/조회 라우트 신설 + dcy-002 tag 정책 v1 일관 적용.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~11 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md (REQ-032)
|
||||
- Modules: app/api/v1_portfolios.py, app/schemas/v1.py, app/services/portfolio_service.py, app/repositories/* — 최초 지정
|
||||
- Tests: tests/test_v1_portfolios_tags_comments.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 계획 편입; dcy-002 REQ-001/003/005 선행 의존성 + 본 REQ와의 범위 경계 확정
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~11 검증; ownership + dcy-002 tag 정책 위반 + disabled 차단 시나리오
|
||||
@@ -0,0 +1,113 @@
|
||||
# REQ-031: portfolio comment CRUD v1 라우트
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- v1 API에 portfolio comment CRUD 경로(`/v1/portfolios/{portfolio_id}/comments`)를 신설해 user/admin이 HTTP로 portfolio에 코멘트를 작성·관리할 수 있도록 한다.
|
||||
- ownership 규칙과 disabled 차단을 REQ-026/027과 동일 패턴으로 적용해 v1 portfolio surface 인가 일관성을 확보한다.
|
||||
|
||||
## Requirement
|
||||
- 신규 라우트 (Bearer 인증 필수):
|
||||
- `POST /v1/portfolios/{portfolio_id}/comments` — body: `{"content": "<text>"}`. 응답 HTTP 201 + 생성된 comment.
|
||||
- `GET /v1/portfolios/{portfolio_id}/comments` — 응답 HTTP 200 + comment 목록(created_at desc 순).
|
||||
- `PATCH /v1/portfolios/{portfolio_id}/comments/{comment_id}` — body: `{"content": "<updated>"}`. 응답 HTTP 200 + 갱신된 comment.
|
||||
- `DELETE /v1/portfolios/{portfolio_id}/comments/{comment_id}` — 응답 HTTP 200 또는 204.
|
||||
- **ownership 규칙** (REQ-026/030과 동일):
|
||||
- `token_type ∈ {service, client}` → HTTP 403 fail-closed.
|
||||
- `permission='admin'` → 모든 portfolio.
|
||||
- `permission ∈ {read, trade-write}` → 자기 소유 portfolio만, 불일치 시 HTTP 403.
|
||||
- **disabled portfolio 차단** (REQ-027 정합): `status='disabled'`인 portfolio에 대해 POST/PATCH/DELETE는 HTTP 409 또는 422로 거부. GET은 허용.
|
||||
- 비존재 `portfolio_id` 또는 `comment_id`는 HTTP 404.
|
||||
- 기존 `CommentCreateRequest`/`CommentUpdateRequest`/`CommentResponse` schema 재사용 또는 v1 전용 래핑(`PortfolioCommentRequest`, `PortfolioCommentResponse`).
|
||||
- 기존 service 메서드(`create_comment`, `list_comments`, `update_comment`, `delete_comment`) 재사용.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given session token + 본인 active portfolio, When `POST /v1/portfolios/{id}/comments` body `{"content": "..."}`, Then HTTP 201 + 생성된 comment.
|
||||
2. Given session token + 본인 portfolio + 적용된 comments, When `GET /v1/portfolios/{id}/comments`, Then HTTP 200 + 코멘트 목록(created_at desc).
|
||||
3. Given session token + 본인 portfolio + 본인 comment, When `PATCH /v1/portfolios/{id}/comments/{comment_id}` body `{"content": "..."}`, Then HTTP 200 + 갱신된 comment.
|
||||
4. Given session token + 본인 portfolio + 본인 comment, When `DELETE /v1/portfolios/{id}/comments/{comment_id}`, Then HTTP 200/204.
|
||||
5. Given session token + 타 user portfolio, When 임의 동사 호출, Then HTTP 403 (ownership 불일치).
|
||||
6. Given service/client token, When 임의 동사 호출, Then HTTP 403 fail-closed.
|
||||
7. Given session token + disabled portfolio, When `POST`/`PATCH`/`DELETE`, Then HTTP 409 또는 422.
|
||||
8. Given session token + disabled portfolio, When `GET /v1/portfolios/{id}/comments`, Then HTTP 200 + 기존 comments 목록 반환(조회는 허용).
|
||||
9. Given admin token + 타 user portfolio, When 임의 동사 호출, Then HTTP 200/201/204 (admin은 모든 portfolio 허용).
|
||||
10. Given Bearer token 없음, When 호출, Then HTTP 401.
|
||||
11. Given 비존재 `portfolio_id` 또는 `comment_id`, When 호출, Then HTTP 404.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — `/v1/portfolios/{id}/comments` 명세 (REQ-032)
|
||||
- Modules:
|
||||
- `app/api/v1_portfolios.py` — comment 서브 라우트 추가
|
||||
- `app/schemas/v1.py` — comment 관련 스키마 (기존 `app/schemas/portfolio.py:CommentCreateRequest`/`CommentUpdateRequest`/`CommentResponse` 재사용 또는 v1 래핑)
|
||||
- `app/services/portfolio_service.py:create_comment`/`list_comments`/`update_comment`/`delete_comment` — 기존 메서드 재사용 + disabled 차단 검증 추가
|
||||
- Tests:
|
||||
- 신규/갱신 `tests/test_v1_portfolios_tags_comments.py` — AC-1~11 (comment 부분)
|
||||
|
||||
## Notes
|
||||
- comment 작성자 ownership(본인 comment만 수정/삭제 가능)은 기존 portfolio_comments 테이블 구조에 따라 처리. dcy-003 본문은 portfolio ownership만 명시하고 comment 작성자 ownership은 별도 결정 사항 — 본 REQ에서는 portfolio ownership을 일관 적용하고 comment 작성자 ownership은 batch design 단계에서 확정.
|
||||
- 본 REQ는 dcy-003 FR-8 산출물. tag(REQ-030)와 별도 REQ로 분리해 동사 surface를 명확히 함.
|
||||
- Source: dcy-003 FR-8, 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-8 산출물.
|
||||
|
||||
#### Intent
|
||||
- v1 portfolio comment CRUD 라우트 신설 + ownership/disabled 검증 일관 적용.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~11 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/interface-contract.md (REQ-032)
|
||||
- Modules: app/api/v1_portfolios.py, app/schemas/v1.py, app/services/portfolio_service.py — 최초 지정
|
||||
- Tests: tests/test_v1_portfolios_tags_comments.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 계획 편입; comment 작성자 ownership 정책은 batch design 단계에서 확정
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~11 검증; ownership + disabled 차단 + admin 분기 시나리오
|
||||
@@ -0,0 +1,100 @@
|
||||
# REQ-017: service-token rotate unique index 보강
|
||||
|
||||
- Status: Approved
|
||||
- Type: Migration
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 현행 `idx_auth_tokens_service_name_unique` partial index가 `WHERE token_type='service'` 조건만 적용해 revoked row와 새 row가 같은 name으로 unique 충돌을 일으켜 `pfhub service-token rotate`가 실 DB에서 insert 실패하는 결함을 정상화한다(REL-006 verification 실패 기록).
|
||||
- index 조건에 `AND revoked_at IS NULL`을 추가해 active service token name만 unique 유지하도록 보강한다.
|
||||
|
||||
## Requirement
|
||||
- Alembic migration 신규 작성: 기존 `idx_auth_tokens_service_name_unique`를 drop하고 `(name)` partial index를 `WHERE token_type='service' AND revoked_at IS NULL` 조건으로 재생성한다.
|
||||
- migration은 `CREATE INDEX CONCURRENTLY` 적용으로 운영 lock 영향을 최소화한다(autocommit 외부 트랜잭션 필요 — Alembic op.execute로 직접 SQL 실행).
|
||||
- `app/repositories/postgres.py:161-167` `ensure_schema()`의 `idx_auth_tokens_service_name_unique` 정의도 동일 조건으로 갱신한다.
|
||||
- 기존 active service token 행 데이터에는 변경 없음. 데이터 backfill 불요.
|
||||
- rotate 동작은 기존 코드(`app/services/auth_service.py:rotate_service_token`, `app/repositories/postgres.py:1502-1549`)를 변경하지 않으며, index 보강만으로 정상 작동해야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given 신규 Alembic revision 적용 후, When `pfhub service-token issue NAME --user U --no-expiry` → `pfhub service-token rotate NAME --force` 시퀀스 실행, Then 두 명령 모두 unique 위반 없이 성공한다(실 DB `docker-compose.local-test.yml` 환경).
|
||||
2. Given AC-1 시퀀스 실행 후, When `SELECT name, token_type, revoked_at FROM auth_tokens WHERE name = '<NAME>'`, Then revoked row 1건 + active row 1건이 존재한다.
|
||||
3. Given AC-1 rotate 결과, When 새 token의 `runtime_id`와 `daemon_id`를 확인, Then runtime_id는 갱신, daemon_id는 유지(rotate invariant 보존).
|
||||
4. Given `ensure_schema()` 단독 실행, When 새 partial index 조건이 적용, Then 동일 조건(`WHERE token_type='service' AND revoked_at IS NULL`)이 PostgreSQL에 반영된다.
|
||||
5. Given migration이 운영 환경에서 적용, When CREATE INDEX CONCURRENTLY가 실행, Then auth_tokens write 작업이 long-running lock 없이 진행된다(staging 환경 사전 검증).
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/data-model.md` — auth_tokens 인덱스 변경 반영 (REQ-019에서 처리)
|
||||
- Modules:
|
||||
- `migrations/versions/` — 신규 Alembic revision (예: `20260520_NNNN_service_token_unique_index_fix.py`). `op.execute("DROP INDEX IF EXISTS ...")` + `op.execute("CREATE UNIQUE INDEX CONCURRENTLY ... WHERE token_type='service' AND revoked_at IS NULL")` 패턴
|
||||
- `app/repositories/postgres.py:161-167` — `ensure_schema()`의 idx 정의 조건 갱신
|
||||
- Tests:
|
||||
- 신규 `tests/test_service_token_rotate_integration.py` (실 DB 의존 마킹) — AC-1~3 검증
|
||||
- 기존 `tests/test_daemon_registry.py::test_rotate_*` — 신규 index 조건에서 통과 확인
|
||||
|
||||
## Notes
|
||||
- 이 결함은 REL-006 verification에 "rotate insert 막힘" 검증 실패로 이미 기록됨([temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md:74](temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md#L74)).
|
||||
- CREATE INDEX CONCURRENTLY는 transactional block 밖에서 실행 필요. Alembic revision에서 `autocommit_block`을 사용하거나 `op.execute()`로 직접 SQL 실행해야 한다.
|
||||
- 기존 운영 환경에 revoked service token이 있다면 새 partial index 생성 시점에 자동 적용되며, 별도 데이터 정리 불요.
|
||||
- Source: dcy-004 FR-2, dcy-004 NFR-2/NFR-3/NFR-6, dcy-004 OQ-2 결정(partial 조건 추가).
|
||||
|
||||
## 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-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-2 (rotate unique index 보강) 산출물.
|
||||
|
||||
#### Intent
|
||||
- rotate 실 DB 실패 결함 해소 + R-2(migration lock) 완화 적용.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/data-model.md (REQ-019에서 처리)
|
||||
- Modules: migrations/versions/, app/repositories/postgres.py — 최초 지정
|
||||
- Tests: tests/test_service_token_rotate_integration.py (신규), tests/test_daemon_registry.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 계획 편입; staging 환경에서 CONCURRENTLY migration dry-run으로 lock 시간 측정
|
||||
|
||||
#### Validation Plan
|
||||
- 실 DB(`docker-compose.local-test.yml`) 환경에서 AC-1~3 시퀀스 검증; staging 환경에서 AC-5 lock 측정
|
||||
+23
-2
@@ -74,7 +74,7 @@
|
||||
|
||||
## Next Requirement ID
|
||||
|
||||
- Current: `REQ-016`
|
||||
- Current: `REQ-033`
|
||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||
|
||||
## Requirement Register
|
||||
@@ -93,12 +93,33 @@
|
||||
| REQ-010 | admin asset catalog CRUD | Interface | Approved | High | Justin Ko | [req-010](./Interface/req-010_admin-asset-catalog-crud.md) |
|
||||
| REQ-011 | portfolios asset_id FK 추가 및 symbol/market 제거 | Data | Approved | High | Justin Ko | [req-011](./Data/req-011_portfolios-asset-id-fk-and-column-removal.md) |
|
||||
| REQ-012 | portfolio 생성 CLI catalog 검증 | Interface | Approved | High | Justin Ko | [req-012](./Interface/req-012_portfolio-create-catalog-validation.md) |
|
||||
| REQ-013 | GET /v1/assets 토큰 타입별 분기 재정의 | Interface | Approved | High | Justin Ko | [req-013](./Interface/req-013_get-v1-assets-token-branching.md) |
|
||||
| REQ-013 | GET /v1/assets 토큰 타입별 분기 재정의 | Interface | Deprecated | High | Justin Ko | [req-013](./Interface/req-013_get-v1-assets-token-branching.md) |
|
||||
| REQ-014 | portfolios 데이터 migration | Migration | Approved | High | Justin Ko | [req-014](./Migration/req-014_portfolios-data-migration.md) |
|
||||
| REQ-015 | asset catalog 도입 baseline 문서 갱신 | Documentation | Approved | Low | Justin Ko | [req-015](./Documentation/req-015_asset-catalog-baseline-docs-update.md) |
|
||||
| REQ-016 | client token 발급 경로 코드 폐지 | Interface | Approved | High | Justin Ko | [req-016](./Interface/req-016_client-token-issuance-deprecation.md) |
|
||||
| REQ-017 | service-token rotate unique index 보강 | Migration | Approved | High | Justin Ko | [req-017](./Migration/req-017_service-token-rotate-unique-index-fix.md) |
|
||||
| REQ-018 | POST /auth/service-tokens body daemon_id forward 보정 | Interface | Approved | High | Justin Ko | [req-018](./Interface/req-018_service-token-http-daemon-id-forward.md) |
|
||||
| REQ-019 | 토큰 surface baseline 문서 정합성 회복 | Documentation | Approved | Medium | Justin Ko | [req-019](./Documentation/req-019_token-surface-baseline-docs-consistency.md) |
|
||||
| REQ-020 | 토큰 발급 권한 매트릭스 정의 | Documentation | Approved | Medium | Justin Ko | [req-020](./Documentation/req-020_token-issuance-authorization-matrix.md) |
|
||||
| REQ-021 | 토큰 만료·회전 정책 정의 | Documentation | Approved | Medium | Justin Ko | [req-021](./Documentation/req-021_token-expiry-and-rotation-policy.md) |
|
||||
| REQ-022 | daemon deactivation 시 바인딩 service token 자동 revoke | Interface | Implemented | High | Justin Ko | [req-022](./Interface/req-022_daemon-deactivation-auto-revoke.md) |
|
||||
| REQ-023 | NULL daemon_id legacy token 마이그레이션 CLI | Interface | Implemented | Medium | Justin Ko | [req-023](./Interface/req-023_null-daemon-token-migration-cli.md) |
|
||||
| REQ-024 | env admin token 운영 SOP 정의 | Documentation | Approved | Medium | Justin Ko | [req-024](./Documentation/req-024_env-admin-token-rotation-sop.md) |
|
||||
| REQ-025 | POST /v1/portfolios 신설 (토큰 분기 + catalog 검증) | Interface | Approved | High | Justin Ko | [req-025](./Interface/req-025_v1-portfolios-create.md) |
|
||||
| REQ-026 | PATCH /v1/portfolios/{portfolio_id} 신설 | Interface | Approved | High | Justin Ko | [req-026](./Interface/req-026_v1-portfolios-update.md) |
|
||||
| REQ-027 | DELETE /v1/portfolios/{portfolio_id} 신설 (soft delete) | Interface | Approved | High | Justin Ko | [req-027](./Interface/req-027_v1-portfolios-soft-delete.md) |
|
||||
| REQ-028 | GET /v1/portfolios 신설 (토큰 분기 조회) | Interface | Approved | High | Justin Ko | [req-028](./Interface/req-028_v1-portfolios-list.md) |
|
||||
| REQ-029 | POST /v1/portfolios/{portfolio_id}/ledger/trades 이전 | Interface | Approved | High | Justin Ko | [req-029](./Interface/req-029_v1-portfolios-ledger-trades-migration.md) |
|
||||
| REQ-030 | portfolio tag 적용/해제/조회 v1 라우트 | Interface | Approved | Medium | Justin Ko | [req-030](./Interface/req-030_v1-portfolios-tags-crud.md) |
|
||||
| REQ-031 | portfolio comment CRUD v1 라우트 | Interface | Approved | Medium | Justin Ko | [req-031](./Interface/req-031_v1-portfolios-comments-crud.md) |
|
||||
| REQ-032 | portfolio v1 baseline 문서 갱신 | Documentation | Approved | Medium | Justin Ko | [req-032](./Documentation/req-032_portfolio-v1-baseline-docs-update.md) |
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-05-20: REQ-016~032 (17건) 일괄 Proposed → Approved 전환 — confirm-req 무인자 일괄 모드 실행, 승인 게이트(필수 필드/AC 구체성/Change Log) 모두 충족, blocker 없음. 각 REQ에 CHG-20260520-02 entry 추가
|
||||
- 2026-05-20: REQ-025~032 최초 생성 — dcy-003 (User Portfolio Mutation v1 API 이전, confirmed 2026-05-20) 후 draft-req 실행. POST/PATCH/DELETE/GET /v1/portfolios + ledger path 이전 + tag/comment v1 + baseline 정합성 (FR-1~10 분해, FR-9는 REQ-013 Deprecated로 이미 처리됨)
|
||||
- 2026-05-20: REQ-016~024 최초 생성 — dcy-004 (토큰 발급 및 관리 전면 정비, confirmed 2026-05-20) 후 draft-req 실행. client token 폐지 / rotate index 보강 / HTTP daemon_id forward / baseline 정합성 / 발급 권한 매트릭스 / TTL·회전 정책 / daemon deactivation 자동 revoke / NULL daemon 마이그레이션 CLI / env admin SOP
|
||||
- 2026-05-20: REQ-013 Approved → Deprecated — dcy-003(confirmed 2026-05-20) 옵션 B(path 명명 분리) 결정으로 통합 endpoint 정의 무효화. 후속: `GET /v1/assets`는 REQ-010 보강으로 catalog 전용, `GET /v1/portfolios`는 dcy-003 draft-req 단계에서 신규 REQ로 작성
|
||||
- 2026-05-19: REQ-001~015 전체 Proposed → Approved 전환 — confirm-req 일괄 승인; 승인 게이트(필수 필드, AC 구체성, Change Log) 모두 충족
|
||||
- 2026-05-19: REQ-009~015 최초 생성 — dcy-001 (자산 관리 구조 재설계) confirm-discovery 후 draft-req 실행
|
||||
- 2026-05-19: REQ-001~008 최초 생성 — dcy-002 (태그 소유권 및 적용 대상 정책) confirm-discovery 후 draft-req 실행
|
||||
|
||||
Reference in New Issue
Block a user