chore: restructure SRS for asset catalog and tag model redesign (dcy-001/002, REQ-001~008)

Architecture Redesign (May 19, 2026):
- Separate admin-managed asset catalog from user portfolios
- Eliminate portfolio as asset definition; clarify portfolio as user position
- Introduce new asset_catalog + portfolio structure

Discovery Documents:
- dcy-001: asset-catalog-and-user-portfolio-model — admin asset management and user portfolio separation
- dcy-002: tag-ownership-and-applicability-policy — tag model implementation strategy

New Requirements (REQ-001~008):
- REQ-001: tags master table introduction (Data, Proposed)
- REQ-002: assets.tag_ids UUID[] column addition (Data, Proposed)
- REQ-003: portfolios.tag_ids and portfolio_tags removal (Data, Proposed)
- REQ-004: tags master HTTP API (Interface, Proposed)
- REQ-005: tag apply/remove HTTP API (Interface, Proposed)
- REQ-006: portfolio_tags data migration (Migration, Proposed)
- REQ-007: pfhub tag CLI commands (Interface, Proposed)
- REQ-008: tag model baseline docs update (Documentation, Proposed)

Cleanup:
- Remove archived SRS documents (REQ-001~029 old structure)
- Remove archived batch documentation (BAT-001~010)
- Remove archived discovery documents (dcy-001~006 old structure)
- Remove archived release documentation (REL-001~006)

Data Model Updates:
- Add Deployment category to SRS structure
- Add Data category for new data model requirements
- Update app/cli/core/utils.py for new tag handling

Documentation:
- Create new SRS index with consolidated requirement structure
- Update docs/discovery/index.md with new discovery references
- Migrate from requirement-centric to domain-model-centric organization

Rationale:
- Existing single-portfolio model lacks admin catalog governance
- New asset catalog enables central management of tradeable symbols
- Tag system supports user-driven asset organization and discovery
- Cleaner separation of concerns between admin operations and user trading
This commit is contained in:
2026-05-19 13:54:25 +00:00
parent 47c4f9bfde
commit 1082f041a1
96 changed files with 1133 additions and 9303 deletions
@@ -0,0 +1,74 @@
# REQ-001: tags 마스터 테이블 도입
- Status: Proposed
- Type: Data
- Priority: High
- Owner: Justin Ko
## Intent
- 태그를 자유 문자열이 아닌 정규화된 마스터 테이블로 관리하여 소유권(created_by_user_id), admin 구분(is_admin_tag), 생명주기(is_active)를 추적할 수 있는 기반을 마련한다.
- is_admin_tag는 태그 생성 시점의 권한을 스냅샷으로 기록하며 이후 권한 변경과 무관하게 고정된다.
- 태그 삭제는 is_active = FALSE soft delete로만 수행하여 기존 참조가 archived 상태임을 확인할 수 있게 한다.
## Requirement
- 시스템은 다음 스키마로 `tags` 테이블을 생성해야 한다: `(tag_id UUID PK DEFAULT gen_random_uuid(), name TEXT NOT NULL, created_by_user_id TEXT NOT NULL REFERENCES users(id), is_admin_tag BOOLEAN NOT NULL DEFAULT FALSE, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()), UNIQUE(name)`
- 태그 생성 시 요청자가 admin 권한(Permission.ADMIN)이면 is_admin_tag = TRUE로 기록하며 이후 이 값을 변경하지 않는다.
- 태그 비활성화는 is_active = FALSE로만 수행하며 hard DELETE는 사용하지 않는다.
## Acceptance Criteria
1. Given Alembic 마이그레이션이 적용된 DB, When `tags` 테이블 스키마를 조회, Then tag_id (UUID NOT NULL PK), name (TEXT NOT NULL UNIQUE), created_by_user_id (TEXT NOT NULL REFERENCES users.id), is_admin_tag (BOOLEAN NOT NULL DEFAULT FALSE), is_active (BOOLEAN NOT NULL DEFAULT TRUE), created_at (TIMESTAMPTZ NOT NULL DEFAULT NOW()) 컬럼이 모두 존재한다.
2. Given admin 권한 토큰으로 태그 생성 요청, When 태그 행이 INSERT, Then is_admin_tag = TRUE이고 이후 권한이 변경되어도 tags.is_admin_tag는 변경되지 않는다.
3. Given 일반(non-admin) 사용자 토큰으로 태그 생성 요청, When 태그 행이 INSERT, Then is_admin_tag = FALSE이다.
4. Given 활성화된 태그, When 비활성화 요청, Then is_active = FALSE로 설정되며 해당 행은 DB에서 삭제되지 않는다.
5. Given is_active = FALSE인 태그, When 태그를 조회, Then 행이 반환되며 비활성 상태를 확인할 수 있다.
## Impacted Area
- Docs: docs/data-model.md (tags 테이블 추가)
- Modules:
- migrations/versions/ — tags 테이블 생성 Alembic revision
- app/domain/models.py — Tag 도메인 모델 추가
- app/repositories/portfolio_repository.py — tags CRUD 메서드 프로토콜 추가
- app/repositories/postgres.py — tags INSERT/SELECT/UPDATE SQL 구현
- Tests: tags 테이블 스키마 검증, is_admin_tag 스냅샷 동작 검증
## Notes
- created_by_user_id는 REFERENCES users(id)로 외래키를 가진다. users 테이블은 기존에 존재한다 (migration 0004).
- is_admin_tag 판단은 런타임 JOIN 없이 tags 테이블 컬럼만으로 수행된다 (dcy-002 A-1).
- name UNIQUE 제약은 TEXT 수준에서 적용되며, 소문자 정규화는 애플리케이션 레이어(REQ-004)에서 수행한다.
- Source: dcy-002 FR-1, NFR-1(부분), OQ-4
## Change Log
### CHG-20260519-01
- Date: 2026-05-19
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
#### Intent
- dcy-002 FR-1을 구현 가능한 REQ 단위로 정규화
#### Acceptance Criteria Delta
- Added:
- AC-1~5 최초 작성
#### Impacted Area Delta
- Docs:
- docs/data-model.md — tags 테이블 추가
- Modules:
- migrations/versions/, app/domain/models.py, app/repositories/ — 최초 지정
- 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 계획 편입
@@ -0,0 +1,73 @@
# REQ-002: assets.tag_ids UUID[] 컬럼 추가
- Status: Proposed
- Type: Data
- Priority: High
- Owner: Justin Ko
## Intent
- assets(portfolios) 테이블에 UUID[] 배열 컬럼을 추가하여 junction 테이블 없이 tags 마스터 테이블 기반 태그 참조를 저장한다.
- GIN 인덱스로 배열 조회 성능을 확보하고, 비활성화된 태그는 조용히 제거하지 않고 archived 상태로 반환하여 이력 추적을 가능하게 한다.
- 태그 적용 정책 검증(is_admin_tag=TRUE AND is_active=TRUE 확인, 403 반환)은 REQ-005에서 구현한다.
## Requirement
- portfolios 테이블(asset에 해당)에 `tag_ids UUID[] NOT NULL DEFAULT '{}'` 컬럼을 추가해야 한다.
- portfolios.tag_ids에 GIN 인덱스를 생성해야 한다.
- tag_ids 배열에 is_active = FALSE인 tag_id가 포함된 경우, 해당 항목을 조용히 제거하지 않고 archived 상태임을 명시하여 반환해야 한다.
## Acceptance Criteria
1. Given Alembic 마이그레이션이 적용된 DB, When portfolios 테이블 스키마를 조회, Then tag_ids UUID[] NOT NULL DEFAULT '{}' 컬럼이 존재한다.
2. Given 마이그레이션이 적용된 DB, When portfolios.tag_ids 인덱스를 조회, Then GIN 인덱스가 존재한다.
3. Given asset의 tag_ids 배열에 is_active = FALSE인 tag_id가 포함, When 해당 asset의 태그 목록을 조회, Then 비활성 태그가 archived 표시와 함께 반환되며 배열에서 자동 제거되지 않는다.
4. Given tag_ids = '{}' 기본값인 asset, When 태그가 적용되지 않은 상태로 조회, Then 빈 배열이 반환된다.
## Impacted Area
- Docs: docs/data-model.md (portfolios 스키마에 tag_ids 컬럼 추가)
- Modules:
- migrations/versions/ — portfolios.tag_ids 컬럼 추가 및 GIN 인덱스 생성 Alembic revision
- app/domain/models.py — Portfolio 도메인 모델에 tag_ids 필드 추가
- app/repositories/postgres.py — tag_ids 배열 조회 시 archived 표시 로직
- app/schemas/v1.py — AssetItem에 tag_ids 필드 추가
- Tests: portfolios 테이블 스키마 검증, GIN 인덱스 존재 확인, archived 표시 동작 확인
## Notes
- dcy-001의 assets 테이블(portfolios)이 확정된 이후 이 REQ를 구현해야 한다 (dcy-002 R-2).
- 배열 참조 무결성은 애플리케이션 레이어에서 보장한다 (dcy-002 NFR-2). PostgreSQL ARRAY FK는 미사용.
- portfolios 테이블이 v1 API의 "asset" 개념에 해당한다 (portfolio_id = asset_id in v1 API).
- Source: dcy-002 FR-2, NFR-2
## Change Log
### CHG-20260519-01
- Date: 2026-05-19
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
#### Intent
- dcy-002 FR-2의 스키마 변경 부분을 독립 구현 단위로 분리
#### Acceptance Criteria Delta
- Added:
- AC-1~4 최초 작성
#### Impacted Area Delta
- Docs:
- docs/data-model.md — tag_ids 컬럼 추가
- Modules:
- migrations/versions/, app/domain/models.py, app/repositories/postgres.py, app/schemas/v1.py — 최초 지정
- 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 계획 편입
@@ -0,0 +1,73 @@
# REQ-003: portfolios.tag_ids UUID[] 컬럼 추가 및 portfolio_tags 테이블 제거
- Status: Proposed
- Type: Data
- Priority: High
- Owner: Justin Ko
## Intent
- portfolios 테이블에 UUID[] 배열 컬럼을 추가하고 기존 자유 문자열 기반 portfolio_tags 테이블을 제거하여 tags 마스터 테이블 기반 태그 참조 구조로 전환한다.
- 이 REQ는 스키마 변경에만 집중하며, 기존 portfolio_tags 데이터 이전 절차는 REQ-006에서 다룬다.
- GIN 인덱스로 배열 조회 성능을 확보하고, 비활성 태그는 archived로 표시한다.
## Requirement
- portfolios 테이블에 `tag_ids UUID[] NOT NULL DEFAULT '{}'` 컬럼을 추가해야 한다.
- portfolios.tag_ids에 GIN 인덱스를 생성해야 한다.
- REQ-006 데이터 마이그레이션 완료 후 portfolio_tags 테이블을 DROP해야 한다.
- portfolios.tag_ids에 is_active = FALSE인 tag_id가 포함된 경우, archived 상태임을 명시하여 반환해야 하며 자동 제거하지 않는다.
## Acceptance Criteria
1. Given Alembic 마이그레이션(REQ-006 포함)이 적용된 DB, When portfolios 테이블 스키마를 조회, Then tag_ids UUID[] NOT NULL DEFAULT '{}' 컬럼이 존재한다.
2. Given 마이그레이션이 적용된 DB, When portfolios.tag_ids 인덱스를 조회, Then GIN 인덱스가 존재한다.
3. Given 마이그레이션이 적용된 DB, When DB 스키마를 조회, Then portfolio_tags 테이블이 존재하지 않는다.
4. Given portfolio의 tag_ids 배열에 is_active = FALSE인 tag_id가 포함, When 해당 portfolio의 태그 목록을 조회, Then 비활성 태그가 archived 표시와 함께 반환되며 배열에서 자동 제거되지 않는다.
## Impacted Area
- Docs: docs/data-model.md (portfolios 스키마에 tag_ids 추가, portfolio_tags 테이블 제거 반영)
- Modules:
- migrations/versions/ — portfolios.tag_ids 컬럼 추가, GIN 인덱스 생성, portfolio_tags DROP Alembic revision
- app/domain/models.py — Portfolio 도메인 모델에 tag_ids 필드 추가
- app/repositories/postgres.py — portfolio tag_ids 배열 조작 메서드, archived 표시 로직
- app/schemas/ — PortfolioItem에 tag_ids 필드 추가 (해당 스키마 존재 시)
- Tests: portfolios 테이블 스키마 검증, portfolio_tags 테이블 부재 확인, GIN 인덱스 존재 확인, archived 표시 동작
## Notes
- REQ-006(portfolio_tags 데이터 마이그레이션)이 선행되어야 한다. portfolio_tags DROP은 데이터 마이그레이션 완료 후 수행.
- 기존 `pfhub portfolio tag add/list/remove` 명령어는 portfolio_tags 테이블에 의존하므로 이 REQ와 함께 비활성화/제거한다 (REQ-007 참조).
- Source: dcy-002 FR-3, NFR-2
## Change Log
### CHG-20260519-01
- Date: 2026-05-19
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
#### Intent
- dcy-002 FR-3의 스키마 변경을 독립 REQ 단위로 분리
#### Acceptance Criteria Delta
- Added:
- AC-1~4 최초 작성
#### Impacted Area Delta
- Docs:
- docs/data-model.md — portfolio_tags 제거, tag_ids 추가
- Modules:
- migrations/versions/, app/domain/models.py, app/repositories/postgres.py, app/schemas/ — 최초 지정
- 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-006 선행 의존성 확인
@@ -1,109 +0,0 @@
# req-020: 실배포 admin bootstrap 및 steady-state operator access runbook
- Status: Approved
- Type: Deployment
- Priority: High
- Owner: repository-maintainers
## Intent
- 실배포 환경에서 admin auth context를 확보하는 경로가 문서화되지 않으면, 운영자는 release smoke를 재현 가능하게 수행할 수 없고 bootstrap과 steady-state access를 혼동해 보안 사고가 발생할 수 있다.
- README.md Production Operator Runbook에 admin bootstrap(신규 환경/disaster recovery 전용), steady-state canonical access(운영자 개인 admin 로그인), break-glass fallback(env admin token) 3-tier 경로를 명시하고 각 경로의 사용 조건을 구분한다.
## Requirement
- README.md Production Operator Runbook 섹션은 다음 3-tier 경로를 조건과 함께 구분하여 문서화해야 한다.
1. **bootstrap path**: `PORTFOLIOHUB_BOOTSTRAP_*` 기반 초기 사용자 생성. `users` 테이블이 비어 있는 신규 환경 또는 disaster recovery 직후 admin 사용자 부재가 확인된 경우에만 허용.
2. **steady-state canonical path**: 운영자 소유 admin 사용자 계정으로 `/auth/login`하여 DB-issued admin session 또는 동일 컨텍스트에서 발급한 client token 확보. 이 admin context로 `POST /auth/service-tokens`를 호출해 release smoke용 read/trade-write service-token을 발급.
3. **break-glass fallback**: 환경 변수 기반 admin token을 `/opt/portfoliohub/.env`에서 꺼내는 경로. 운영자 로그인 경로가 모두 막혔을 때만 허용. 정기 smoke의 표준 경로로 사용 금지.
- 각 경로의 사용 조건, 운영 책임 역할(deployment secret custodian, production operator/release approver), secret 보관 위치(`/opt/portfoliohub/.env` / `EnvironmentFile`)를 명시한다.
- raw credential, raw token, bootstrap password는 README 또는 release 문서에 기록하지 않는다.
## Acceptance Criteria
1. Given README.md Production Operator Runbook 섹션이 존재할 때, When 운영자가 섹션을 읽을 때, Then bootstrap path / steady-state canonical path / break-glass fallback이 각각 사용 조건과 함께 구분되어 기술되어 있어야 한다.
2. Given `users` 테이블이 비어 있지 않은 실배포 환경에서, When `PORTFOLIOHUB_BOOTSTRAP_*` 기반 bootstrap 로직이 실행될 때, Then 기존 사용자를 덮어쓰거나 중복 생성하지 않고 bootstrap을 건너뛰어야 한다 (idempotency 보장).
3. Given README runbook에 역할 책임이 명시되어 있을 때, When 운영자가 runbook을 참조할 때, Then deployment secret custodian과 production operator/release approver 역할이 구분되어 있고 secret 보관 위치(`/opt/portfoliohub/.env`)가 명시되어 있어야 한다.
4. Given 운영자가 steady-state canonical path를 따를 때, When `/auth/login` → `POST /auth/service-tokens` 순서로 실행할 때, Then admin session token 기반 service-token 발급 성공 경로가 runbook에 기술되어 있어야 한다.
## Impacted Area
- Docs: `README.md` — Production Operator Runbook 섹션 (3-tier 경로 구분 및 역할 책임 보강)
- Modules: `app/core/config.py` (bootstrap 환경 변수 운영 제약 확인), `app/main.py` (bootstrap idempotency 동작 확인)
- Tests: bootstrap idempotency는 기존 구현 확인으로 대체 가능. 별도 신규 테스트 불필요.
## Notes
- NFR-1 (raw credential 비노출): 이 REQ의 핵심 제약으로 포함됨. Release 문서, README 등 모든 운영 문서에서 raw value 기록 금지.
- NFR-3 (bootstrap idempotency): `app/main.py`에서 `users` 테이블이 비어 있을 때만 bootstrap 사용자를 생성하는 로직이 기존에 구현되어 있음. AC-2는 기존 구현 확인으로 검증한다.
- `/auth/login` 엔드포인트가 실제 구현되어 있어야 steady-state canonical path가 완성된다. 현재 저장소에 로그인 엔드포인트가 없으면 해당 AC는 구현 시 재검토가 필요하다.
- 이 REQ는 기능 추가보다 운영 절차 문서화가 주 목적이다. 코드 변경이 필요할 경우 최소 범위로 제한한다.
- Source Discovery: dcy-005 FR-1
## Change Log
### CHG-20260512-02
- Date: 2026-05-12
- Author: repository-maintainers
#### Change Summary
- Status: Proposed → Approved (confirm-req 게이트 통과)
#### Intent
- 필수 필드, AC, Notes blocker 없음 확인 후 승인 전환
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- 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: none
#### Validation Plan
- 승인 전환만 수행, 추가 검증 불필요
### CHG-20260512-01
- Date: 2026-05-12
- Author: repository-maintainers
#### Change Summary
- REQ 초안 생성 (dcy-005 FR-1 기반)
#### Intent
- 실배포 admin bootstrap 및 steady-state access 경로 운영 문서화 요구사항 정의
#### Acceptance Criteria Delta
- Added:
- AC-1~4 초안 생성
#### Impacted Area Delta
- Docs:
- README.md Production Operator Runbook 섹션
- Modules:
- app/core/config.py, app/main.py (확인 범위)
- Tests:
- 없음 (기존 구현 확인으로 대체)
#### Delivery Trace Delta
- Batches:
- none (미배정)
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: none
#### Validation Plan
- README.md Production Operator Runbook 섹션의 3-tier 경로 구분 확인
- app/main.py bootstrap idempotency 코드 리뷰로 AC-2 검증
@@ -1,148 +0,0 @@
# req-003: 로컬 DB 운영 runbook 및 baseline 문서 갱신
- Status: Implemented
- Type: Documentation
- Priority: Medium
- Owner: repository-maintainers
## Intent
- 이 요구사항은 로컬 docker compose 기반 DB 검증 결과를 운영자가 반복 사용할 수 있는 runbook과 baseline 문서로 정리하기 위한 문서화 backlog를 정의한다.
- README, docs/runtime-flows.md, docs/project-structure.md에 로컬 DB 준비 절차, release gate 기준, rollback 또는 재시도 판단 기준을 반영하는 것이 목표다.
## Requirement
- 프로젝트는 로컬 docker compose 기반 DB 검증 범위를 반영해 README, docs/runtime-flows.md, docs/project-structure.md를 갱신해야 하며, init-db와 migrate-db 실행 흐름, release gate 증거, rollback 또는 재시도 판단 기준을 운영자가 추적 가능한 runbook 형태로 정리해야 한다.
## Acceptance Criteria
1. Given 로컬 DB validation 범위가 확정되어 있을 때, When README를 갱신하면, Then docker compose 기반 준비 절차, init-db와 migrate-db 실행 순서, health-check 및 smoke flow 검증 기준이 포함되어 있어야 한다.
2. Given Alembic와 runtime ownership 경계가 정리될 때, When docs/project-structure.md와 docs/runtime-flows.md를 갱신하면, Then 구조 제약과 실행 흐름 제약이 baseline 문서에 반영되어 있어야 한다.
3. Given 이번 Discovery의 release gate가 로컬 DB validation 기준으로 확정되어 있을 때, When 운영 runbook을 작성하면, Then rollback 또는 재시도 판단 기준과 남겨야 할 검증 로그가 문서에 명시되어 있어야 한다.
## Impacted Area
- Docs: README.md, docs/project-structure.md, docs/runtime-flows.md, docs/discovery/dcy-001_20260430_real-db-migration-validation-and-operations-readiness.md
- Modules: app/cli/commands/health.py, app/db/migrations.py, app/repositories/postgres.py
- Tests: 문서 기반 수동 검증 체크리스트와 release gate evidence review 절차
## Notes
- 이 요구사항은 dcy-001의 FR-3 일부, FR-4, NFR-3, S-3을 구현 가능한 Documentation 단위로 정규화한 것이다.
- baseline 문서 전체를 복제하지 않고, 로컬 DB 검증과 직접 관련된 구조 제약과 흐름 제약만 업데이트 대상으로 삼는다.
- 성공 지표의 세부 artifact 위치는 REQ 승인 또는 batch planning에서 확정할 수 있다.
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-007 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-007 verification.md AC-1~3 모두 PASS. Blocking Issues 없음. README.md "Local DB Validation Runbook"(rollback 판단 기준, release gate criteria 포함), docs/project-structure.md "Schema Ownership Boundary", docs/runtime-flows.md 흐름 갱신 evidence 연결 확인 완료.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-007 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-007)
#### Validation Plan
- BAT-007 verification.md AC-1~3 evidence 연결 확인 완료
### CHG-20260430-02
- Date: 2026-04-30
- Author: GitHub Copilot
#### Change Summary
- confirm-req 승인 게이트를 통과해 이 REQ를 Approved로 전환했다.
#### Intent
- dcy-001의 delivery input으로 사용할 수 있는 확정 requirement로 승격하기 위해 상태를 변경했다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- docs/srs/index.md
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: approved
- Follow-up Action: suggest-batch-reqs 또는 draft-batch 단계에서 REQ-003의 runbook 산출물 범위를 delivery scope로 연결한다.
#### Validation Plan
- REQ 본문 필수 섹션, Acceptance Criteria 구체성, docs/srs/index.md 상태 동기화를 확인한다.
### CHG-20260430-01
- Date: 2026-04-30
- Author: GitHub Copilot
#### Change Summary
- dcy-001에서 로컬 DB 운영 runbook 및 baseline 문서 갱신 요구사항을 신규 Proposed REQ로 생성했다.
#### Intent
- 운영 준비 산출물과 baseline 문서 갱신 요구를 독립 Documentation backlog로 관리하기 위해 생성했다.
#### Acceptance Criteria Delta
- Added:
- README 로컬 DB 검증 절차 갱신 기준 정의
- docs/project-structure.md와 docs/runtime-flows.md baseline 갱신 기준 정의
- rollback 또는 재시도 판단 기준과 로그 수집 기준 정의
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- README.md
- docs/project-structure.md
- docs/runtime-flows.md
- docs/discovery/dcy-001_20260430_real-db-migration-validation-and-operations-readiness.md
- Modules:
- app/cli/commands/health.py
- app/db/migrations.py
- app/repositories/postgres.py
- Tests:
- 문서 기반 수동 검증 체크리스트와 release gate evidence review 절차
#### 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 단계에서 baseline 문서 갱신 범위와 runbook 산출물 위치를 검토하고 승인한다.
#### Validation Plan
- confirm-req 전까지 Acceptance Criteria가 dcy-001의 FR-3 일부, FR-4, NFR-3, S-3와 일치하는지 검토하고, 이후 batch 단계에서 README 및 baseline 문서 갱신안을 작성한다.
@@ -0,0 +1,73 @@
# REQ-008: 태그 모델 도입 baseline 문서 갱신
- Status: Proposed
- Type: Documentation
- Priority: Low
- Owner: Justin Ko
## Intent
- dcy-002에서 정의한 tags 마스터 테이블, UUID[] 배열 방식, HTTP API, CLI 변경 내용을 project-structure, runtime-flows, data-model, interface-contract 문서에 반영하여 baseline을 최신 상태로 유지한다.
- 구현 완료 후 문서가 실제 코드와 일치하지 않으면 다음 Discovery/REQ 작성 시 잘못된 baseline 참조로 이어질 수 있으므로 구현 완료 시점에 함께 갱신한다.
## Requirement
- docs/data-model.md에 tags 테이블 스키마, assets(portfolios).tag_ids UUID[] 컬럼, portfolio_tags 테이블 제거를 반영해야 한다.
- docs/interface-contract.md에 /v1/tags (POST/GET/PATCH), /v1/assets/{asset_id}/tags (POST/DELETE), /v1/portfolios/{portfolio_id}/tags (POST/DELETE) 엔드포인트 명세를 추가해야 한다.
- docs/project-structure.md에 pfhub tag 커맨드 그룹 추가, app/api/ 태그 모듈 추가, pfhub portfolio tag 제거를 반영해야 한다.
- docs/runtime-flows.md에 tag 관련 CLI 흐름(create, list, deactivate, apply, remove)과 HTTP 흐름을 추가해야 한다.
## Acceptance Criteria
1. Given 구현 완료 후, When docs/data-model.md를 검토, Then tags 테이블 스키마(tag_id, name, created_by_user_id, is_admin_tag, is_active, created_at), portfolios.tag_ids UUID[] 컬럼, portfolio_tags 테이블 제거가 반영되어 있다.
2. Given 구현 완료 후, When docs/interface-contract.md를 검토, Then /v1/tags (POST/GET/PATCH) 및 /v1/assets/{id}/tags, /v1/portfolios/{id}/tags (POST/DELETE) 엔드포인트 명세가 포함되어 있다.
3. Given 구현 완료 후, When docs/project-structure.md를 검토, Then pfhub tag 커맨드 그룹이 CLI Command groups에 포함되고 app/api/ 섹션에 태그 API 모듈이 반영되어 있다.
4. Given 구현 완료 후, When docs/runtime-flows.md를 검토, Then pfhub tag 커맨드 실행 흐름과 /v1/tags 관련 HTTP 흐름이 문서화되어 있다.
## Impacted Area
- Docs:
- docs/data-model.md
- docs/interface-contract.md
- docs/project-structure.md
- docs/runtime-flows.md
- Modules: 없음 (문서 전용)
- Tests: 없음 (문서 검토로 확인)
## Notes
- 이 REQ는 REQ-001~007 구현 완료 후 마지막으로 수행한다.
- data-model.md와 interface-contract.md는 dcy-002 영향 범위에 명시된 문서이다.
- project-structure.md와 runtime-flows.md는 baseline 참조 문서로, 신규 CLI 커맨드 그룹과 API 흐름이 추가되면 함께 갱신해야 한다 (docs/project-structure.md Update Triggers: "새 top-level runtime 또는 domain 모듈이 추가되었을 때").
- Source: dcy-002 영향 범위, skill 규칙 0(baseline 문서 갱신 REQ)
## Change Log
### CHG-20260519-01
- Date: 2026-05-19
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
#### Intent
- dcy-002 영향 범위의 문서 갱신 요구를 Documentation REQ로 분리; baseline 문서 갱신 의무 명시
#### Acceptance Criteria Delta
- Added:
- AC-1~4 최초 작성
#### Impacted Area Delta
- Docs:
- docs/data-model.md, docs/interface-contract.md, docs/project-structure.md, 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: REQ-001~007 구현 완료 후 일괄 반영
@@ -1,176 +0,0 @@
# req-011: trex-basic 계약 반영을 위한 baseline 문서 갱신
- Status: Approved
- Type: Documentation
- Priority: Medium
- Owner: repository-maintainers
## Intent
- dcy-002 구현 완료 후 interface-contract.md, data-model.md, runtime-flows.md를 실제 구현 결과와 동기화한다.
- dcy-006의 daemon-aware 변경으로 baseline 문서는 runtime_id 중심 설명을 daemon binding/assignment 모델로 다시 정렬해야 한다.
- 이 REQ는 구현이 완료된 시점에 수행하는 문서 갱신이며, 구현 전 선행 작업이 아니다.
## Requirement
- REQ-005~009, REQ-012의 daemon-aware 변경이 구현 완료(Implemented)된 시점에 다음 3개 baseline 문서를 갱신해야 한다.
1. docs/interface-contract.md: `POST /auth/service-tokens`, `GET /v1/assets`, `GET /v1/user_info`, `POST /v1/assets/{asset_id}/ledger/trades`의 daemon binding / daemon-scoped contract, `403` mismatch 정책, service-token bearer 인증 방식을 반영
2. docs/data-model.md: `daemons` 테이블, `auth_tokens.daemon_id`, `portfolios.managed_by_daemon_id`, `auth_tokens.runtime_id`의 역할 분리와 transition `NULL` backfill 정책 반영
3. docs/runtime-flows.md: daemon registry binding, `/v1/*` daemon-scoped read/write 흐름, rotate 시 `daemon_id` 유지 + `runtime_id` 갱신 불변식 반영
## Acceptance Criteria
1. Given docs/interface-contract.md를 검토하면, Then `POST /auth/service-tokens`, `GET /v1/assets`, `GET /v1/user_info`, `POST /v1/assets/{asset_id}/ledger/trades`의 daemon-aware 요청/응답 명세와 `403` mismatch 정책이 포함되어야 한다.
2. Given docs/data-model.md를 검토하면, Then `daemons`, `broker_credentials`, `ledger_trades` 테이블과 `portfolios.market`, `portfolios.managed_by_daemon_id`, `auth_tokens.runtime_id`, `auth_tokens.daemon_id`가 문서화되어야 한다.
3. Given docs/runtime-flows.md를 검토하면, Then daemon registry binding 흐름, daemon-scoped v1 API 처리 흐름, rotate 시 `runtime_id`/`daemon_id` 역할 차이가 포함되어야 한다.
## Impacted Area
- Docs: docs/interface-contract.md, docs/data-model.md, docs/runtime-flows.md
- Modules: 없음
- Tests: 없음
## Notes
- 이 REQ는 구현 완료 후 수행하므로 batch 설계 단계에서 별도 implementation task로 명시한다.
- 문서 갱신 내용은 draft-batch-verification 단계에서 실제 구현과 비교해 검증한다.
- README.md 운영 provenance / release 절차 변경은 REQ-021 및 후속 dcy-006 REQ에서 다룬다.
- Source Discovery: dcy-002 (전체 FR/NFR 구현 완료 후 documentation closure), dcy-006 (FR-5, FR-6, NFR-1)
## Change Log
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- baseline 문서 REQ를 daemon-aware v1 contract 반영 범위로 확장하고, 기존 Implemented 상태를 Approved로 되돌림
#### Intent
- dcy-006은 interface-contract, data-model, runtime-flows가 모두 daemon binding/assignment와 runtime_id/daemon_id 역할 분리를 반영하도록 요구한다. BAT-008/REL-002 문서 evidence는 runtime_id 중심 baseline만 다뤘으므로 revised REQ를 충족하지 못한다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- AC-1~3: daemon-aware contract, data model, runtime flow 기준으로 재정의
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `docs/interface-contract.md`, `docs/data-model.md`, `docs/runtime-flows.md`
- Modules:
- none
- Tests:
- none (문서 리뷰 및 delivery verification evidence 중심)
#### Delivery Trace Delta
- Batches:
- BAT-008 (release-candidate; REL-002 released input) — 기존 verification evidence는 daemon-aware baseline을 포함하지 않아 재작성 필요
- Releases:
- REL-002 (released) — released baseline docs scope가 revised REQ 대비 불완전
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes
- Status Recommendation: rollback-to-approved
- Follow-up Action: dcy-006 후속 batch에서 daemon-aware baseline docs를 갱신하고 BAT-008에 대응하는 새 verification evidence를 작성한다.
#### Validation Plan
- dcy-006 FR-5/FR-6와 현행 baseline 문서를 대조해 누락된 daemon contract 항목을 식별한다.
- 구현 후 문서 리뷰와 batch verification에서 interface/data/runtime 3축이 모두 갱신됐는지 확인한다.
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-008 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-008 verification.md에서 REQ-011 AC-1~3의 문서화 기준과 evidence가 모두 PASS로 확인되어 Implemented로 승격한다.
#### Acceptance Criteria Delta
- Added: none
- Updated: none
- Removed: none
#### Impacted Area Delta
- Docs: none
- Modules: none
- Tests: none
#### Delivery Trace Delta
- Batches:
- BAT-008 (release-candidate)
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release 시 BAT-008 포함 여부 검토
#### Validation Plan
- BAT-008 verification.md의 REQ-011 AC-1~3 evidence와 Ready for Release: true 확인
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-011 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 3개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### 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: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch. REQ-004~010 Implemented 전환 후 실행.
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-011 최초 생성 (source Discovery: dcy-002 문서화 클로저)
#### Intent
- dcy-002 구현 완료 후 baseline 문서 갱신 요구사항을 독립 Documentation REQ로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md
- docs/data-model.md
- docs/runtime-flows.md
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. REQ-004~010 모두 Implemented 전환 후 이 REQ를 별도 구현 task로 수행.
@@ -1,145 +0,0 @@
# req-019: portfolio UUID 전환 관련 baseline 문서 갱신
- Status: Implemented
- Type: Documentation
- Priority: Medium
- Owner: repository-maintainers
## Intent
- REQ-016~018의 구현으로 변경되는 DB 스키마, CLI 흐름, HTTP API 계약을 baseline 문서(`docs/project-structure.md`, `docs/runtime-flows.md`, `docs/data-model.md`, `docs/interface-contract.md`)에 반영해 문서와 구현 간 불일치를 방지한다.
- trex-basic 동시 배포 계획을 `docs/interface-contract.md`에 기록한다.
## Requirement
- `docs/project-structure.md`를 갱신해 `app/repositories/postgres.py`에 신규 추가된 `get_portfolio_by_name_and_user()` 메서드와 `app/domain/models.py`의 `portfolio_id`(UUID 문자열) 의미 변경을 반영한다.
- `docs/runtime-flows.md`를 갱신해 아래를 반영한다.
- CLI 명령 흐름: `<name>` + `--user <username>` 조합으로 portfolio를 식별하고 내부에서 UUID를 조회하는 흐름
- `v1 API — GET /v1/assets` flow: `asset_id` 필드가 UUID를 반환함
- `docs/data-model.md`를 갱신해 `portfolios` 테이블의 `portfolio_id`(UUID, TEXT, PRIMARY KEY), `name`(TEXT) 신규 컬럼, `(owner_user_id, name)` 복합 UNIQUE 제약을 반영한다.
- `docs/interface-contract.md`를 갱신해 `GET /v1/assets` `asset_id` 필드가 UUID를 반환함을 기록하고, trex-basic과 portfolio-hub의 동시 배포 계획을 명시한다.
## Acceptance Criteria
1. Given `docs/project-structure.md`를 검토하면, Then `app/repositories/postgres.py` 설명에 `get_portfolio_by_name_and_user()` 메서드가 언급되고, `app/domain/models.py`의 `portfolio_id`가 UUID 기반임이 반영된다.
2. Given `docs/runtime-flows.md`를 검토하면, Then CLI 명령 흐름 설명에 `<name>` + `--user <username>` 조합 식별 방식이 반영되고, `v1 API — GET /v1/assets` flow에 `asset_id`가 UUID임이 명시된다.
3. Given `docs/data-model.md`를 검토하면, Then `portfolios` 테이블 스키마에 `portfolio_id`(UUID TEXT, PK), `name`(TEXT), `(owner_user_id, name)` UNIQUE 제약이 반영된다.
4. Given `docs/interface-contract.md`를 검토하면, Then `GET /v1/assets` `asset_id` 필드가 UUID를 반환함이 명시되고, trex-basic과 portfolio-hub의 동시 배포 계획이 기록된다.
## Impacted Area
- Docs:
- `docs/project-structure.md`
- `docs/runtime-flows.md`
- `docs/data-model.md`
- `docs/interface-contract.md`
- Modules: 없음 (문서 전용)
- Tests: 없음 (문서 전용)
## Notes
- 이 REQ는 REQ-016, REQ-017, REQ-018 구현 완료 후 또는 동일 batch에서 함께 처리하는 것이 적절하다.
- trex-basic 측 코드 변경은 이번 범위 밖이며, 동시 배포 계획을 문서화하는 것만 이 REQ 범위에 포함된다.
- NFR-1(단일 revision, IF NOT EXISTS, atomic)은 REQ-016에 흡수. NFR-2(단일 DB 쿼리, 오류 메시지)는 REQ-017에 흡수. NFR-3(동시 배포 계획 기록)은 이 REQ에서 처리.
- Source Discovery: dcy-004_20260511_portfolio-id-uuid-migration-and-user-scoped-name-resolution (FR-6, NFR-3)
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- 최초 생성 — dcy-004 draft-req에 의해 생성
#### Intent
- dcy-004 Discovery의 FR-6(docs/project-structure.md, docs/runtime-flows.md 갱신), NFR-3(동시 배포 계획 docs/interface-contract.md 기록) 반영. docs/data-model.md, docs/interface-contract.md 갱신 포함.
#### Acceptance Criteria Delta
- Added:
- 1~4 (최초 생성)
#### Impacted Area Delta
- Docs:
- docs/project-structure.md
- docs/runtime-flows.md
- docs/data-model.md
- docs/interface-contract.md
#### Delivery Trace Delta
- Batches: none (batch 미결정)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req → suggest-batch-reqs → draft-batch
#### Validation Plan
- 각 baseline 문서에 변경 내용이 포함됐는지 코드 리뷰로 확인
### CHG-20260511-02
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Approved 전환 — confirm-req 게이트 통과
#### Intent
- Delivery 입력으로 사용할 수 있도록 REQ-019를 승인 상태로 승격한다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- none
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs로 batch grouping 검토
#### Validation Plan
- confirm-req 게이트 재평가 결과: 필수 메타데이터, 구체적 Acceptance Criteria, Impacted Area, Change Log, blocker 없음 확인
### CHG-20260511-03
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-006 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-006 verification.md의 REQ-019 AC-1~4 모두 baseline 문서 변경 내용 직접 확인으로 evidence 충족. 문서 전용 REQ로 테스트 불필요. Blocking Issues 없음.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-006 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-006)
#### Validation Plan
- BAT-006 verification.md AC-1~4: project-structure.md / runtime-flows.md / data-model.md / interface-contract.md 각 문서 변경 내용 manual 확인 완료
@@ -1,154 +0,0 @@
# req-021: release 검증 credential matrix 및 token provenance 운영 절차
- Status: Approved
- Type: Documentation
- Priority: High
- Owner: repository-maintainers
## Intent
- release 검증에 사용하는 credential의 용도, 발급 주체, 보관 위치, 폐기 절차가 정의되지 않으면 smoke 재현성이 없어지고 secret leakage 위험이 높아진다.
- public/no-auth, read service-token, trade-write service-token, admin auth context 4개 credential 관점의 matrix를 문서화하고, daemon-aware CLI/API를 token provenance 확인 canonical path로 명시한다.
## Requirement
- README.md Production Operator Runbook 또는 연결된 운영 문서는 아래 credential matrix를 명시해야 한다.
- **public/no-auth**: `GET /health`, `/docs`, `/openapi.json`. 인증 불필요. 배포 직후 공개 smoke에 사용.
- **read service-token**: `GET /v1/assets`, `GET /v1/user_info`. 환경 단위 운영 credential. 발급: admin context로 `POST /auth/service-tokens` 후 existing `daemon_id`에 binding. 보관: `/opt/portfoliohub/.env` 또는 동일 접근 통제 하 운영 절차. 공동 소유자: release approver / repository-maintainers.
- **trade-write service-token**: `POST /v1/assets/{asset_id}/ledger/trades`. 환경 단위 운영 credential. 발급: admin context로 `POST /auth/service-tokens` 후 existing `daemon_id`에 binding. 보관: read token과 동일 정책. 공동 소유자: release approver / repository-maintainers.
- **admin auth context**: `POST /auth/service-tokens`. 운영자 개인 소유. 공유 금지. 발급: steady-state는 `/auth/login` 경유 DB-issued session, break-glass는 env token.
- release 문서에는 name, token_prefix, permission, `daemon_id`, `runtime_id`, issued_at, revoked_at만 기록하고 raw token값은 기록하지 않는다.
- 운영자는 `pfhub service-token list`, `pfhub service-token issue`, `pfhub service-token rotate`, `pfhub user get`, `GET /v1/user_info`를 token provenance 확인 canonical support path로 사용한다. 각 명령의 출력 필드(name, token_prefix, token_id, user_id, permission, `daemon_id`, created_at, last_used_at, revoked_at, `runtime_id`)를 provenance 확인 기준으로 명시한다.
- rotate 또는 재발급 이후에는 `daemon_id` 불변과 `runtime_id` 갱신 여부를 함께 대조해야 한다.
## Acceptance Criteria
1. Given README.md 또는 연결된 운영 문서가 존재할 때, When 운영자가 credential matrix를 참조할 때, Then public/read/trade-write/admin 4개 credential의 용도, 발급 주체, 보관 위치, 공동 소유 역할과 daemon binding 전제가 구분되어 기술되어 있어야 한다.
2. Given release 문서(docs/releases/*.md)가 존재할 때, When smoke evidence를 기록할 때, Then name, token_prefix, permission, `daemon_id`, `runtime_id`, issued_at, revoked_at 필드만 남기고 raw token 값은 포함되지 않아야 한다.
3. Given 운영자가 service-token provenance를 확인할 때, When `pfhub service-token list`를 실행할 때, Then name, permission, `daemon_id`, created_at, last_used_at, revoked_at 필드가 출력되고, `GET /v1/user_info`의 `runtime_id`, `daemon_id`와 대조해 배포된 토큰의 provenance를 재현 가능하게 확인할 수 있어야 한다.
4. Given admin auth context가 필요한 smoke를 수행할 때, When 운영자가 credential matrix를 따를 때, Then admin context는 개인 소유로 취급되고 read/trade-write token은 release approver 역할과 공동 소유 형태로 관리되는 것이 문서에 명시되어 있어야 한다.
5. Given 운영자가 rotate 또는 재발급 후 provenance를 재확인할 때, When `pfhub service-token rotate` 또는 issue 결과를 검토할 때, Then `daemon_id`는 유지 또는 요청한 binding 값으로 확인되고 `runtime_id`는 새 값으로 대조 가능해야 한다.
## Impacted Area
- Docs: `README.md` — credential matrix 항목 및 daemon-aware token provenance CLI 절차 추가/보강
- Modules: `app/api/v1_user_info.py` (`runtime_id`, `daemon_id` 반환 확인), `app/cli/commands/service_tokens.py` (issue/list/rotate 출력 필드 확인)
- Tests: 없음 (문서화 중심 REQ)
## Notes
- NFR-1 (raw credential 비노출): 이 REQ의 핵심 제약. AC-2에 명시.
- FR-4와 FR-2를 통합한 REQ. token provenance 확인 수단과 credential matrix는 운영자가 같은 맥락에서 참조하므로 분리 시 중복이 발생한다.
- `runtime_id`는 service-token rotate 후 변경되고 `daemon_id`는 유지되므로, 문서에는 두 식별자의 역할 차이를 함께 명시해야 한다.
- daemon provisioning 자체는 별도 관리 경로로 수행하고, provenance 절차는 binding 결과 확인에 집중한다.
- Source Discovery: dcy-005 FR-2, FR-4; dcy-006 FR-5, FR-6
## Change Log
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- token provenance REQ를 daemon-aware support path와 `daemon_id` evidence 필드 기준으로 보강
#### Intent
- dcy-006은 issue/list/rotate와 `/v1/user_info`가 모두 `daemon_id`를 노출하고, release provenance도 `runtime_id` 단독이 아니라 daemon binding을 함께 추적하도록 요구한다. 기존 Approved 본문은 runtime_id 중심이라 운영 절차 기준이 불완전하다.
#### Acceptance Criteria Delta
- Added:
- AC-5: rotate/re-issue 후 `daemon_id`/`runtime_id` 대조 절차
- Updated:
- AC-1~3: daemon binding 전제와 `daemon_id` evidence 필드 반영
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `README.md` credential matrix / provenance 절차
- Modules:
- `app/api/v1_user_info.py`, `app/cli/commands/service_tokens.py`
- Tests:
- none (문서화 중심 REQ)
#### Delivery Trace Delta
- Batches:
- BAT-009 (release-candidate; REL-005 confirmed input) — README provenance 절차와 release evidence field 정의를 daemon-aware 기준으로 재검토해야 함
- Releases:
- REL-005 (confirmed) — 배포 전 daemon-aware provenance field와 rotate invariant를 반영하도록 scope 재검토 필요
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: BAT-009 구현/verification 전에 README와 release evidence 템플릿을 daemon-aware 필드 기준으로 조정한다.
#### Validation Plan
- README.md와 release evidence 예시가 `daemon_id`, `runtime_id`, rotate invariant를 모두 설명하는지 코드 리뷰로 확인한다.
- REQ-005, REQ-007, REQ-012 문구와 provenance 절차가 식별자 역할 측면에서 일관적인지 대조한다.
### CHG-20260512-02
- Date: 2026-05-12
- Author: repository-maintainers
#### Change Summary
- Status: Proposed → Approved (confirm-req 게이트 통과)
#### Intent
- 필수 필드, AC, Notes blocker 없음 확인 후 승인 전환
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- 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: none
#### Validation Plan
- 승인 전환만 수행, 추가 검증 불필요
### CHG-20260512-01
- Date: 2026-05-12
- Author: repository-maintainers
#### Change Summary
- REQ 초안 생성 (dcy-005 FR-2, FR-4 통합 기반)
#### Intent
- release 검증 credential matrix 및 token provenance 확인 절차 문서화 요구사항 정의
#### Acceptance Criteria Delta
- Added:
- AC-1~4 초안 생성
#### Impacted Area Delta
- Docs:
- README.md credential matrix 및 token provenance 절차
- 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: none
#### Validation Plan
- README.md credential matrix 항목 구분 및 token provenance CLI 절차 존재 확인
- release 문서 기록 정책 준수 여부 확인
@@ -1,114 +0,0 @@
# req-022: 운영 로그 접근 및 release evidence 기록 절차
- Status: Approved
- Type: Documentation
- Priority: Medium
- Owner: repository-maintainers
## Intent
- 운영자가 startup/request 로그 접근 경로와 auth/permission/resource 오류 판독 방법을 모르면 release smoke의 증적을 재현 가능하게 수집할 수 없고, 장애 시 원인 분류가 불가능해진다.
- systemd journal 기반 canonical log access 경로와 401/403/404 구분 판독 기준, release evidence 기록 필드를 README.md에 정의한다.
## Requirement
- README.md Production Operator Runbook 섹션은 아래 로그 접근 및 evidence 기록 절차를 포함해야 한다.
1. **애플리케이션 startup/runtime 로그**: `sudo journalctl -u portfoliohub -n 200 --no-pager` 또는 `-f` 옵션으로 실시간 확인. primary log path.
2. **reverse proxy/access 로그**: `sudo journalctl -u caddy -n 200 --no-pager` 또는 `-f` 옵션. primary log path.
3. **Loki**: `LOKI_ENABLED`와 실제 수집 파이프라인이 운영 환경에서 확인된 경우에만 보조 집계 경로로 취급.
4. **auth/permission/resource 오류 구분**:
- 401: 인증 정보 없음 또는 만료 — bearer token 미제공 또는 유효하지 않은 token
- 403: 권한 부족 — token은 유효하나 해당 엔드포인트에 필요한 permission 미보유
- 404: 리소스 없음 — 인증/권한은 통과했으나 대상 리소스가 DB에 존재하지 않음
5. **release evidence 기록 필드**: 시각(executed_at, completed_at), 요청 경로, HTTP 상태 코드, 소스 시스템(journalctl unit name), 상관 가능한 요청 식별자. raw secret, raw token 값은 포함하지 않는다.
- canonical acceptance smoke는 production에서 수행하고, staging smoke는 optional preflight로만 사용한다.
- 절차의 source of truth는 README runbook으로 유지하고, 실행 evidence의 canonical record는 해당 release 문서에 남긴다.
## Acceptance Criteria
1. Given README.md Production Operator Runbook이 존재할 때, When 운영자가 로그 접근 절차를 참조할 때, Then `journalctl -u portfoliohub`와 `journalctl -u caddy`를 각각 어느 용도로 사용하는지가 명시되어 있어야 한다.
2. Given release smoke 중 HTTP 오류가 발생했을 때, When 운영자가 오류를 분류할 때, Then README runbook에 401(인증 없음), 403(권한 부족), 404(리소스 없음)의 의미가 분리되어 재현 가능하게 설명되어 있어야 한다.
3. Given release 문서에 evidence를 기록할 때, When smoke 실행 결과를 기재할 때, Then 시각, 요청 경로, HTTP 상태 코드, 소스 시스템 필드가 포함되고 raw secret은 포함되지 않아야 한다.
4. Given canonical acceptance 환경이 명시되어 있을 때, When 운영자가 smoke 환경을 선택할 때, Then production이 canonical acceptance이고 staging은 optional preflight임이 README runbook에 명시되어 있어야 한다.
## Impacted Area
- Docs: `README.md` — Production Operator Runbook 로그 접근 절차 및 오류 구분 기준 보강
- Modules: 없음 (문서화 중심 REQ)
- Tests: 없음
## Notes
- NFR-2 (401/403/404 구분 설명): AC-2에 흡수.
- NFR-1 (raw credential 비노출): AC-3에 흡수.
- 외부 URL만으로는 startup log 접근이 불가능하다는 제약을 README에 명시해야 한다. (REL-001 Deployment Facts 참조)
- Loki 관련 설정(`LOKI_ENABLED`, `LOKI_URL`)은 `app/core/config.py`에 존재하지만 현재 production에서 활성화가 확인되지 않으므로 기본 표준 경로로 다루지 않는다.
- Source Discovery: dcy-005 FR-3
## Change Log
### CHG-20260512-02
- Date: 2026-05-12
- Author: repository-maintainers
#### Change Summary
- Status: Proposed → Approved (confirm-req 게이트 통과)
#### Intent
- 필수 필드, AC, Notes blocker 없음 확인 후 승인 전환
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- 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: none
#### Validation Plan
- 승인 전환만 수행, 추가 검증 불필요
### CHG-20260512-01
- Date: 2026-05-12
- Author: repository-maintainers
#### Change Summary
- REQ 초안 생성 (dcy-005 FR-3 기반)
#### Intent
- 운영 로그 접근 및 release evidence 기록 절차 문서화 요구사항 정의
#### Acceptance Criteria Delta
- Added:
- AC-1~4 초안 생성
#### Impacted Area Delta
- Docs:
- README.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: none
#### Validation Plan
- README.md journalctl 명령 존재 및 401/403/404 구분 기술 확인
- release evidence 기록 필드 정책 준수 확인
@@ -1,98 +0,0 @@
# REQ-028: README.md Daemon Provisioning Runbook 반영
- Status: Approved
- Type: Documentation
- Priority: Medium
- Owner: repository-maintainers
## Intent
- interface-contract, data-model, runtime-flows의 daemon-aware 반영은 REQ-011 CHG-20260513-01에서 이미 다룬다.
- 이 REQ는 REQ-011이 다루지 않는 `README.md` 운영 runbook 영역에 한정해, daemon provisioning 절차와 enforcement enablement gate를 반영한다.
## Requirement
- `README.md` Production Operator Runbook 섹션은 daemon provisioning 절차(daemon create → service-token issue/rotate binding 순서)를 반영해야 한다.
- `README.md`는 daemon-scoped enforcement enablement gate의 required evidence(inventory 완료, dry-run audit zero-active-NULL/zero-mismatch, smoke 검증 통과, release approver 승인)를 명시해야 한다.
- `README.md`는 `runtime_id`와 `daemon_id`의 역할 차이(runtime identity vs stable logical identity)를 운영 provenance 절차에서 구분해 설명해야 한다.
## Acceptance Criteria
1. Given `README.md` Production Operator Runbook을 검토할 때, Then daemon create → service-token issue/rotate binding의 순서와 각 단계 조건이 명시되어 있다.
2. Given `README.md` enforcement enablement gate 섹션을 확인할 때, Then inventory 완료, dry-run audit(zero-active-NULL/zero-mismatch), smoke 검증, release approver 승인의 4가지 required evidence가 모두 명시되어 있다.
3. Given `README.md` 운영 provenance 절차를 확인할 때, Then `runtime_id`(rotate 시 갱신)와 `daemon_id`(rotate 후에도 유지)의 역할 차이가 구분되어 설명되어 있다.
## Impacted Area
- Docs: `README.md` (Production Operator Runbook 섹션)
- Modules: 없음
- Tests: 없음 (문서 검토로 검증)
## Notes
- interface-contract.md, data-model.md, runtime-flows.md의 daemon 반영은 REQ-011 CHG-20260513-01 범위이므로 이 REQ에서 중복 요구하지 않는다.
- enforcement enablement gate의 실제 cutover evidence 수집은 release 단계에서 수행한다. 이 REQ는 절차 문서화만 대상으로 한다.
## Change Log
### CHG-20260513-02
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- Status: Proposed → Approved (confirm-req 게이트 통과)
#### Intent
- 필수 필드, AC, blocker 없음 확인 후 승인 전환
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- Modules: 없음
- Tests: 없음
#### Delivery Trace Delta
- Batches: 미정
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: 없음
#### Validation Plan
- 없음 (상태 전환만)
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- 최초 생성 시 REQ-011과 중복되는 interface-contract/data-model/runtime-flows 항목을 제거하고, README.md 운영 runbook 한정으로 범위 축소
#### Intent
- REQ-011이 다루지 않는 README.md daemon provisioning runbook과 enforcement gate 절차만 별도 REQ로 정의
#### Acceptance Criteria Delta
- Added:
- AC-1~3 전체 신규 추가
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: `README.md` 한정
- Modules: 없음
- Tests: 없음
#### Delivery Trace Delta
- Batches: 미정
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: 없음 (신규 REQ)
#### Validation Plan
- README.md에서 AC별 확인 항목(daemon provisioning 순서, gate evidence 4종, runtime_id/daemon_id 역할 구분)이 존재하는지 코드 리뷰로 검증
@@ -1,124 +0,0 @@
# req-004: GET /health 응답 스키마 확장
- Status: Implemented
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic Consumer-Driven Contract는 GET /health가 status, service, version, checked_at을 반환할 것을 요구한다.
- 현재 구현은 `{"status": "ok"}`만 반환하므로 계약 스키마를 충족하도록 응답을 확장한다.
## Requirement
- GET /health 엔드포인트는 status, service(고정값 "portfolio-hub"), version(앱 버전), checked_at(ISO 8601 KST) 필드를 포함한 JSON을 반환해야 한다.
- status 허용값은 ok, degraded, failed이며 정상 응답은 ok를 반환해야 한다.
## Acceptance Criteria
1. Given 서비스가 정상 동작 중일 때, When GET /health를 요청하면, Then 응답 JSON에 status, service, version, checked_at 필드가 모두 존재해야 한다.
2. Given GET /health 응답을 받을 때, When status 값을 확인하면, Then "ok", "degraded", "failed" 중 하나여야 한다.
3. Given GET /health 응답을 받을 때, When checked_at 형식을 검증하면, Then ISO 8601 형식을 만족해야 한다.
4. Given GET /health 응답을 받을 때, When service 필드를 확인하면, Then 고정값 "portfolio-hub"여야 한다.
5. Given 인증 없이 GET /health를 요청해도, Then 200 응답이 반환되어야 한다(비인증 엔드포인트).
## Impacted Area
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/runtime-flows.md
- Modules: app/api/health.py, app/schemas/ (HealthResponse 스키마 추가)
- Tests: tests/test_health.py (신규) 또는 tests/test_app.py 확장
## Notes
- degraded 상태를 반환하는 트리거 조건은 이번 반복에서 정의하지 않는다(OQ-2 Answered). 스키마에 허용값으로만 포함한다.
- 앱 버전은 pyproject.toml의 version 필드 또는 settings에서 읽어 반환한다. 설계 단계에서 구체적 방법을 확정한다.
- Source Discovery: dcy-002 (FR-1)
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-001 re-verified verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-001 verification.md (re-verified, 2026-05-03) AC-1~5 모두 PASS. Blocking Issues 없음. AC2(degraded/failed 경로)는 REQ Notes에서 이번 반복 OOS로 명시 — 릴리스 블로커 아님.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-001 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-001 기반 release 편성 검토)
#### Validation Plan
- BAT-001 verification.md (re-verified) AC-1~5 evidence 연결 확인 완료
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-004 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 5개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### 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: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-004 최초 생성 (source Discovery: dcy-002 FR-1)
#### Intent
- trex-basic Consumer-Driven Contract의 GET /health 응답 스키마 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md (REQ-011 에서 갱신 예정)
- Modules:
- app/api/health.py
- Tests:
- tests/test_health.py 신규
#### Delivery Trace Delta
- Batches:
- none (batch 미생성 상태)
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정
@@ -0,0 +1,83 @@
# REQ-004: tags 마스터 HTTP API
- Status: Proposed
- Type: Interface
- Priority: High
- Owner: Justin Ko
## Intent
- tags 마스터 테이블에 대한 HTTP API를 제공하여 태그 생성, 목록 조회, 비활성화 기능을 권한 기반으로 제어한다.
- 태그 이름은 저장 전 소문자로 정규화하여 대소문자 변형 중복을 방지한다.
- admin 토큰으로 생성된 태그는 is_admin_tag=TRUE로 기록되며, 이는 asset 태그 적용 정책의 핵심 조건이 된다.
## Requirement
- `POST /v1/tags` — 태그 생성; trade-write 이상 권한 필요; name을 str.lower()로 정규화 후 저장; 중복 name(정규화 후) 시 HTTP 409 반환
- `GET /v1/tags` — 태그 목록 조회; read 이상 권한 필요; `?is_active=true|false` 필터 지원
- `PATCH /v1/tags/{tag_id}/deactivate` — 태그 비활성화; admin 또는 태그 생성자 본인만 가능; 이미 비활성화된 태그에 멱등 처리(재요청 시 200 반환); 존재하지 않는 tag_id는 HTTP 404
## Acceptance Criteria
1. Given trade-write 토큰, When `POST /v1/tags {"name": "TestTag"}`, Then HTTP 201, name="testtag"(소문자 정규화)로 저장된다.
2. Given admin 토큰, When `POST /v1/tags {"name": "adminTag"}`, Then HTTP 201, is_admin_tag=TRUE인 태그가 생성된다.
3. Given non-admin trade-write 토큰, When `POST /v1/tags {"name": "userTag"}`, Then HTTP 201, is_admin_tag=FALSE인 태그가 생성된다.
4. Given "testtag" 태그가 이미 존재, When `POST /v1/tags {"name": "TestTag"}` (대소문자 변형), Then HTTP 409 Conflict가 반환된다.
5. Given read 토큰, When `GET /v1/tags`, Then HTTP 200과 활성 태그 목록이 반환된다.
6. Given read 토큰, When `GET /v1/tags?is_active=false`, Then 비활성 태그만 반환된다.
7. Given admin 토큰, When `PATCH /v1/tags/{tag_id}/deactivate` (본인 생성 또는 타인 생성 무관), Then HTTP 200, is_active=FALSE로 설정된다.
8. Given 태그 생성자(non-admin) 토큰, When `PATCH /v1/tags/{tag_id}/deactivate` (본인 생성 태그), Then HTTP 200 반환된다.
9. Given non-admin 비생성자 토큰, When `PATCH /v1/tags/{tag_id}/deactivate`, Then HTTP 403 Forbidden이 반환된다.
10. Given 존재하지 않는 tag_id, When `PATCH /v1/tags/{tag_id}/deactivate`, Then HTTP 404가 반환된다.
11. Given 이미 is_active=FALSE인 태그, When `PATCH /v1/tags/{tag_id}/deactivate` 재요청, Then HTTP 200 반환(멱등).
## Impacted Area
- Docs: docs/interface-contract.md (/v1/tags 엔드포인트 명세 추가)
- Modules:
- app/api/ — v1_tags.py 라우터 추가 (또는 기존 라우터에 통합)
- app/schemas/v1.py — TagItem, TagCreateRequest, TagListResponse 스키마 추가
- app/services/ — 태그 생성/조회/비활성화 서비스 로직
- app/repositories/portfolio_repository.py — insert_tag, get_tags, deactivate_tag 프로토콜
- app/repositories/postgres.py — 태그 CRUD SQL 구현
- app/core/auth.py — permission 검증 (기존 require_read, require_trade_write, require_admin 재사용)
- Tests: 엔드포인트별 permission 검증, 소문자 정규화, 중복 거부, 비활성화 멱등성
## Notes
- NFR-1 흡수: name 정규화는 서비스/라우터 레이어에서 str.lower() 호출, DB는 TEXT UNIQUE 그대로.
- NFR-3 흡수: 권한 미충족 시 HTTP 403은 기존 permission_dependency 패턴 재사용.
- `POST /v1/tags` 권한: trade-write 이상이면 일반 사용자도 태그 생성 가능하며 is_admin_tag는 admin 여부에 따라 자동 결정.
- `GET /v1/tags` 기본 필터: is_active=true (활성 태그만). ?is_active=false로 비활성 태그 조회 가능.
- Source: dcy-002 FR-4(CRUD 부분), NFR-1, NFR-3
## Change Log
### CHG-20260519-01
- Date: 2026-05-19
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
#### Intent
- dcy-002 FR-4의 tags 마스터 CRUD API를 Interface REQ로 분리
#### Acceptance Criteria Delta
- Added:
- AC-1~11 최초 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md — /v1/tags 명세 추가
- Modules:
- app/api/, app/schemas/v1.py, app/services/, app/repositories/ — 최초 지정
- 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-001 선행 의존성 확인
@@ -1,215 +0,0 @@
# req-005: service-token 발급 시 runtime_id 동시 생성
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic 계열 consumer는 `runtime_id`를 런타임 세대 식별자로 사용하되, asset assignment와 운영 provenance는 stable `daemon_id`를 기준으로 추적해야 한다.
- service-token 발급 시 runtime_id를 새로 생성하면서도, 토큰은 기존 daemon registry에 존재하는 `daemon_id`에 바인딩되어 저장되어야 한다.
- CLI(`pfhub service-token issue`)와 API 엔드포인트 양쪽에서 발급 결과에 `runtime_id`와 `daemon_id`가 함께 포함되어야 한다.
## Requirement
- pfhub service-token issue CLI 커맨드와 대응 API 엔드포인트(`POST /auth/service-tokens`)는 service-token 발급 시 고유 `runtime_id`를 생성하고, 기존 daemon registry에 존재하는 `daemon_id`에 토큰을 바인딩해 auth_tokens에 함께 저장한 뒤 발급 결과(`token`, `runtime_id`, `daemon_id`)를 반환해야 한다.
- 발급 경로는 unknown daemon 자동 생성 또는 upsert를 허용하면 안 되며, binding 대상은 사전에 생성된 daemon registry entry여야 한다.
- `runtime_id`는 발급마다 새로 생성되는 런타임 식별자이고, `daemon_id`는 rotate/재기동과 무관하게 유지되는 stable logical identity다.
## Acceptance Criteria
1. Given `pfhub service-token issue <name> --user <username> --daemon-id <daemon_id>` 실행 시, When 발급이 성공하면, Then 출력에 token 문자열, `runtime_id`, `daemon_id`가 모두 포함되어야 한다.
2. Given API 엔드포인트(`POST /auth/service-tokens`)로 existing `daemon_id`를 지정해 service-token 발급 시, When 성공 응답을 받으면, Then 응답 JSON에 token, `runtime_id`, `daemon_id`가 포함되어야 한다.
3. Given `runtime_id`와 `daemon_id`가 발급된 service-token으로 `GET /v1/user_info`를 호출하면, Then 응답의 `runtime_id`, `daemon_id`가 발급 시 저장된 값과 동일해야 한다.
4. Given 동일 daemon에 대해 두 번의 service-token 발급을 수행하면, Then 각각 다른 `runtime_id`가 반환되어야 하고 `daemon_id`는 요청한 binding 값을 유지해야 한다.
5. Given auth_tokens 테이블을 직접 조회하면, Then 발급된 service-token 행에 `runtime_id`와 `daemon_id` 값이 모두 저장되어 있어야 한다.
6. Given 존재하지 않는 `daemon_id`로 service-token 발급을 요청하면, Then 명시적 validation 오류가 반환되어야 하고 auth_tokens 행은 생성되지 않아야 한다.
## Impacted Area
- Docs: docs/runtime-flows.md, docs/interface-contract.md (REQ-011에서 갱신 예정), README.md token provenance 절차 (REQ-021에서 갱신 예정)
- Modules: app/cli/commands/service_tokens.py, app/schemas/auth.py, app/api/auth_routes.py, app/domain/models.py, app/services/auth_service.py, app/repositories/portfolio_repository.py, app/repositories/postgres.py
- Tests: tests/test_auth.py, tests/test_cli.py, tests/test_v1.py
## Notes
- runtime_id 포맷은 UUID v4를 권장한다. 설계 단계에서 확정한다.
- auth_tokens 테이블의 `runtime_id`, `daemon_id` 컬럼 및 daemon registry 스키마는 REQ-009 migration에서 처리한다.
- daemon provisioning(create/update)은 별도 관리 경로에서 수행하며, issue 경로는 binding만 담당한다.
- 기존 daemon-less service-token은 transition 구간에서 `daemon_id = NULL`로 남을 수 있고, daemon-scoped API에서는 fail-closed로 처리한다.
- Source Discovery: dcy-002 (FR-2, A-2), dcy-006 (FR-1, FR-5, NFR-1)
## Change Log
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- runtime_id-only service-token 발급 요구사항을 existing daemon binding + `daemon_id` diagnostics 포함 구조로 확장하고, 기존 Implemented 상태를 Approved로 되돌림
#### Intent
- dcy-006에서 `runtime_id`는 runtime identity, `daemon_id`는 stable logical identity로 역할이 분리됐다. 현재 BAT-001/REL-001 구현은 runtime_id만 저장·노출하므로 revised issuance contract와 daemon-scoped authorization 전제를 충족하지 못한다.
#### Acceptance Criteria Delta
- Added:
- AC-6: unknown `daemon_id` 발급 요청 거부
- Updated:
- AC-1~5: CLI/API issue, `/v1/user_info`, DB persistence 기준을 `daemon_id` binding 포함으로 재정의
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `docs/runtime-flows.md`, `docs/interface-contract.md` — issue flow와 response contract에 `daemon_id` 추가 필요
- `README.md` — daemon-aware provenance 절차 반영 필요
- Modules:
- `app/cli/commands/service_tokens.py`, `app/schemas/auth.py`, `app/api/auth_routes.py`, `app/services/auth_service.py`, `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`
- Tests:
- `tests/test_auth.py`, `tests/test_cli.py`, `tests/test_v1.py`
#### Delivery Trace Delta
- Batches:
- BAT-001 (release-candidate; REL-001 released input) — runtime_id-only issuance evidence는 daemon binding 요구사항 기준으로 재설계 및 재검증 필요
- Releases:
- REL-001 (released) — service-token issuance release evidence가 revised REQ를 충족하지 않으므로 후속 app-service release가 필요
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes
- Status Recommendation: rollback-to-approved
- Follow-up Action: dcy-006 후속 batch에서 daemon registry binding, issue API/CLI contract, `/v1/user_info` diagnostics를 함께 구현하고 재검증한다.
#### Validation Plan
- dcy-006 FR-1/FR-5와 `app/services/auth_service.py`, `app/api/auth_routes.py`, `app/cli/commands/service_tokens.py`의 현재 구현 차이를 코드 리뷰로 대조한다.
- 구현 후 `tests/test_auth.py`, `tests/test_cli.py`, `tests/test_v1.py`에 daemon-bound issuance/user_info 케이스를 추가해 targeted validation을 수행한다.
### CHG-20260511-02
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- REQ-005 AC-2의 API 경로를 실제 구현 경로인 `POST /auth/service-tokens`로 명확화
#### Intent
- CONFLICT-4 분석 결과를 반영해 "POST /auth/tokens 또는 동등 경로"라는 모호한 표현을 제거하고, BAT-001 verification과 runtime flow에 이미 반영된 실제 엔드포인트로 REQ를 정렬한다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- AC-2: API 발급 경로를 `POST /auth/service-tokens`로 구체화
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `docs/srs/Interface/req-005_service-token-runtime-id-issuance.md`: AC-2 경로 문구 명확화
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- BAT-001 (release-candidate) — verification evidence는 이미 `POST /auth/service-tokens` 기준으로 정렬되어 있어 추가 재검증은 불필요
- Releases:
- REL-001 (draft) — release scope 문서도 동일 경로를 사용 중이라 추가 수정 없음
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: none
#### Validation Plan
- REQ-005 AC-2, BAT-001 verification evidence, runtime-flows.md의 API 경로가 모두 `POST /auth/service-tokens`로 일치하는지 코드 리뷰로 확인
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-001 re-verified verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-001 verification.md (re-verified, 2026-05-03) AC-1~5 모두 PASS. Blocking Issues 없음. AC5 DB 미확인은 REQ-009와 동일 제약으로 non-blocker 처리.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-001 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-001)
#### Validation Plan
- BAT-001 verification.md (re-verified) AC-1~5 evidence 연결 확인 완료
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-005 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 5개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### 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: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-005 최초 생성 (source Discovery: dcy-002 FR-2)
#### Intent
- service-token 발급 시 runtime_id 동시 생성 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/runtime-flows.md (REQ-011에서 갱신 예정)
- Modules:
- app/cli/commands/service_tokens.py
- app/api/auth_routes.py
- app/domain/models.py
- Tests:
- tests/test_auth.py 확장 또는 tests/test_service_tokens.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-009 migration과 함께 delivery 단위 구성 권장.
@@ -0,0 +1,83 @@
# REQ-005: 태그 적용·해제 HTTP API
- Status: Proposed
- Type: Interface
- Priority: High
- Owner: Justin Ko
## Intent
- asset(portfolios)과 portfolio에 태그를 적용하고 해제하는 HTTP API를 제공하며, asset에는 admin 생성 태그만, portfolio에는 admin 또는 소유자 생성 태그만 적용 가능하도록 정책을 적용한다.
- 정책 위반 시 HTTP 403을 반환하여 명확한 오류 피드백을 제공한다.
- 존재하지 않는 tag_id 적용 시도는 HTTP 404로 처리한다.
## Requirement
- `POST /v1/assets/{asset_id}/tags {"tag_id": "..."}` — asset에 태그 적용; admin 권한 필요; 적용 조건: tags.is_admin_tag=TRUE AND tags.is_active=TRUE; 조건 불만족 시 HTTP 403; 존재하지 않는 tag_id 시 HTTP 404
- `DELETE /v1/assets/{asset_id}/tags/{tag_id}` — asset에서 태그 해제; admin 권한 필요
- `POST /v1/portfolios/{portfolio_id}/tags {"tag_id": "..."}` — portfolio에 태그 적용; trade-write 이상 권한 필요; 적용 조건: tags.is_active=TRUE AND (tags.is_admin_tag=TRUE OR tags.created_by_user_id = portfolio 소유자); 조건 불만족 시 HTTP 403; 존재하지 않는 tag_id 시 HTTP 404
- `DELETE /v1/portfolios/{portfolio_id}/tags/{tag_id}` — portfolio에서 태그 해제; trade-write 이상, 소유자 본인 또는 admin
## Acceptance Criteria
1. Given admin 토큰, admin이 생성한 활성 tag_id, When `POST /v1/assets/{asset_id}/tags`, Then HTTP 200, 해당 tag_id가 asset의 tag_ids 배열에 추가된다.
2. Given admin 토큰, is_admin_tag=FALSE인 tag_id, When `POST /v1/assets/{asset_id}/tags`, Then HTTP 403 Forbidden이 반환된다.
3. Given admin 토큰, is_active=FALSE인 tag_id, When `POST /v1/assets/{asset_id}/tags`, Then HTTP 403 Forbidden이 반환된다.
4. Given non-admin 토큰, When `POST /v1/assets/{asset_id}/tags`, Then HTTP 403 Forbidden이 반환된다.
5. Given admin 토큰, When `DELETE /v1/assets/{asset_id}/tags/{tag_id}`, Then HTTP 200, tag_id가 asset의 tag_ids 배열에서 제거된다.
6. Given trade-write 토큰(portfolio 소유자), 소유자 본인이 생성한 tag_id (is_admin_tag=FALSE, is_active=TRUE), When `POST /v1/portfolios/{portfolio_id}/tags`, Then HTTP 200, 태그가 portfolio의 tag_ids에 추가된다.
7. Given trade-write 토큰(portfolio 소유자), admin이 생성한 활성 tag_id, When `POST /v1/portfolios/{portfolio_id}/tags`, Then HTTP 200, 태그가 추가된다.
8. Given trade-write 토큰(user A, 소유자), 다른 user B가 생성한 non-admin tag_id, When `POST /v1/portfolios/{portfolio_id}/tags`, Then HTTP 403 Forbidden이 반환된다.
9. Given 소유자 또는 admin 토큰, When `DELETE /v1/portfolios/{portfolio_id}/tags/{tag_id}`, Then HTTP 200, tag_id가 portfolio의 tag_ids에서 제거된다.
10. Given 유효한 권한, 존재하지 않는 tag_id, When 적용 요청, Then HTTP 404 Not Found가 반환된다.
11. Given 존재하지 않는 asset_id 또는 portfolio_id, When 태그 적용 요청, Then HTTP 404가 반환된다.
## Impacted Area
- Docs: docs/interface-contract.md (/v1/assets/{id}/tags, /v1/portfolios/{id}/tags 엔드포인트 명세 추가)
- Modules:
- app/api/v1_assets.py — POST/DELETE /v1/assets/{asset_id}/tags 라우트 추가
- app/api/portfolios.py — POST/DELETE /v1/portfolios/{portfolio_id}/tags 라우트 추가 (또는 별도 파일)
- app/schemas/v1.py — TagApplyRequest 스키마 추가
- app/services/ — 태그 적용/해제 서비스 로직, 정책 검증
- app/repositories/postgres.py — tag_ids 배열 append/remove SQL (array_append, array_remove 또는 동등)
- Tests: 정책 위반 시 403, 존재하지 않는 tag_id 시 404, admin/non-admin 구분 검증
## Notes
- NFR-3 흡수: 정책 위반 시 HTTP 403은 이 REQ의 핵심 동작.
- REQ-001(tags 테이블), REQ-002(assets.tag_ids), REQ-003(portfolios.tag_ids)이 선행되어야 한다.
- asset에 태그 적용 권한이 admin 전용인 이유: assets는 admin 관리 카탈로그이며 asset 태그에는 is_admin_tag=TRUE만 허용 (dcy-002 FR-2).
- portfolio 소유자 확인: portfolios.owner_user_id = auth_context.user_id 비교.
- Source: dcy-002 FR-2, FR-3, FR-4(apply/remove 부분), NFR-3
## Change Log
### CHG-20260519-01
- Date: 2026-05-19
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
#### Intent
- dcy-002 FR-4의 태그 적용·해제 엔드포인트와 FR-2/FR-3의 정책 검증을 Interface REQ로 분리
#### Acceptance Criteria Delta
- Added:
- AC-1~11 최초 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md — apply/remove 엔드포인트 추가
- Modules:
- app/api/v1_assets.py, app/api/portfolios.py, app/schemas/v1.py, app/services/, app/repositories/postgres.py — 최초 지정
- 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-001, REQ-002, REQ-003 선행 의존성 확인
@@ -1,260 +0,0 @@
# req-006: GET /v1/assets 엔드포인트 구현
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic 데몬이 자신에게 배정된 자산 목록과 전략 파라미터만 조회할 수 있도록 portfolio 데이터를 daemon-scoped asset 뷰로 제공한다.
- 동일 owner user 아래 여러 daemon이 존재해도, 다른 daemon에 배정된 asset은 응답에 섞이면 안 된다.
## Requirement
- GET /v1/assets는 service-token bearer 인증으로 토큰 소유 user 범위 안에서, 인증된 token의 `daemon_id`에 배정된 portfolio 목록만 `asset_id`, `symbol`, `market`, `status`, `cash`, `quantity`, `broker_type`, `strategy_id`, `strategy_type`, `strategy_params`, `updated_at` 필드로 반환해야 한다. 다른 사용자 소유 portfolio와 다른 daemon에 배정된 portfolio는 응답에 포함되면 안 된다.
- `status=active` 쿼리 파라미터는 daemon-scoped 결과 집합 위에서 필터링되어야 한다.
- strategy_params는 해당 portfolio의 active_strategy_id로 조회한 StrategySet.config에서 파생한다. strategy_id는 허용값 검증 없이 그대로 반환한다(OQ-1 Answered).
- strategy_type은 해당 portfolio의 active_strategy_id로 조회한 StrategySet.name을 반환한다. active_strategy_id가 없거나 StrategySet 조회 실패 시 null을 반환한다.
- `daemon_id`가 없는 legacy service-token은 transition 구간 동안 `GET /v1/assets`에서 fail-closed(`403 Forbidden`)로 처리되어야 한다.
## Acceptance Criteria
1. Given 유효한 daemon-bound service-token bearer 인증으로 `GET /v1/assets` 요청 시, When 토큰이 유효하면, Then HTTP 200과 해당 daemon에 배정된 portfolio 목록만 반환되어야 하며 다른 사용자 소유 portfolio나 다른 daemon에 배정된 portfolio는 포함되면 안 된다.
2. Given `GET /v1/assets?status=active` 요청 시, Then status가 active인 항목만 daemon-scoped 결과 안에서 반환되어야 한다.
3. Given 응답의 각 항목을 확인하면, Then asset_id, symbol, market, status, cash, quantity, broker_type, strategy_id, strategy_type, strategy_params, updated_at 필드가 모두 포함되어야 한다.
4. Given portfolio에 연결된 StrategySet이 있을 때, When strategy_params를 확인하면, Then StrategySet.config 값에서 파생된 JSON 객체여야 한다.
5. Given StrategySet config가 없거나 파싱 실패 시, Then strategy_params는 빈 객체({})를 반환하고 에러가 전파되지 않아야 한다.
6. Given portfolios.market이 NULL인 경우, Then 응답의 market 필드도 null로 반환되어야 한다(DECIDE_MARKET_DEFAULT Answered).
7. Given 인증 없이 GET /v1/assets 요청 시, Then HTTP 401이 반환되어야 한다.
8. Given portfolio에 active_strategy_id로 연결된 StrategySet이 있을 때, Then strategy_type은 해당 StrategySet.name 값을 반환해야 한다.
9. Given portfolio에 active_strategy_id가 없거나 StrategySet 조회 실패 시, Then strategy_type은 null을 반환해야 한다.
10. Given `daemon_id`가 없는 legacy service-token으로 `GET /v1/assets` 요청 시, Then HTTP 403이 반환되어야 하고 daemon-scoped asset 목록은 비노출이어야 한다.
## Impacted Area
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/project-structure.md (신규 라우터 파일 반영 필요)
- Modules: app/api/v1_assets.py, app/schemas/v1.py, app/repositories/portfolio_repository.py, app/repositories/postgres.py, app/core/auth.py, app/main.py
- Tests: tests/test_v1.py, tests/test_portfolio_ownership.py 또는 신규 daemon assignment 회귀 테스트
## Notes
- broker_type은 portfolios.broker 컬럼을 그대로 매핑한다(m-2 finding 반영).
- strategy_id는 portfolios.active_strategy_id를 그대로 반환한다.
- `GET /v1/assets`의 최종 authorization 스코프는 service-token 소유자 범위 위에 `managed_by_daemon_id = auth.daemon_id` 조건을 추가한 daemon scope다.
- exact-one asset assignment는 `portfolios.managed_by_daemon_id`를 통해 표현되며 스키마 제약은 REQ-009 change-req에서 다룬다.
- Source Discovery: dcy-002 (FR-3, A-3, OQ-1), dcy-006 (FR-2, FR-3, NFR-2)
## Change Log
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- owner-user scope 기반 `/v1/assets` 요구사항을 daemon-scoped asset visibility와 fail-closed legacy token 규칙으로 갱신하고, Implemented 상태를 Approved로 되돌림
#### Intent
- dcy-006에서 각 asset은 정확히 1개 daemon에 배정되고, `GET /v1/assets`는 owner user 전체가 아니라 인증 daemon 범위만 반환해야 한다. 현재 구현은 `owner_user_id=auth.user_id`만 사용하므로 revised authorization model과 맞지 않는다.
#### Acceptance Criteria Delta
- Added:
- AC-10: daemon-less legacy token fail-closed(403)
- Updated:
- AC-1, AC-2: owner scope를 daemon scope 기준으로 재정의
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `docs/interface-contract.md`, `docs/runtime-flows.md` — daemon-scoped asset list contract 반영 필요
- Modules:
- `app/api/v1_assets.py`, `app/schemas/v1.py`, `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`, `app/core/auth.py`
- Tests:
- `tests/test_v1.py`, daemon assignment 관련 신규 회귀 테스트
#### Delivery Trace Delta
- Batches:
- BAT-001 (release-candidate; REL-001 released input) — owner-user scope verification evidence는 daemon-scoped visibility 기준으로 전면 재검증 필요
- Releases:
- REL-001 (released) — released contract는 owner-user scope 기준이라 revised REQ와 불일치
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes
- Status Recommendation: rollback-to-approved
- Follow-up Action: dcy-006 후속 batch에서 daemon assignment query, 403 fail-closed path, daemon-scoped regression test를 구현한다.
#### Validation Plan
- `app/api/v1_assets.py`가 현재 `owner_user_id=auth.user_id`만 적용하는지 확인하고, daemon-aware query 설계를 REQ-009와 함께 정리한다.
- 구현 후 daemon A/B 분리 시나리오와 daemon-less token 403 시나리오를 targeted pytest로 검증한다.
### CHG-20260511-02
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- owner scope 회귀 수정 반영 — GET /v1/assets는 service-token 소유자의 portfolio만 반환함을 REQ와 verification evidence에 명시
#### Intent
- CONFLICT-1 분석 결과와 2026-05-11 사용자 결정에 따라 trex-basic은 전체 사용자의 portfolio를 조회하면 안 되며, 서비스 토큰 소유자 범위로만 조회되어야 한다는 설계를 REQ 본문과 검증 기준에 명확히 남긴다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- AC1: 반환 범위를 "해당 사용자 소유 portfolio 목록만"으로 명확화하고, 다른 사용자 소유 portfolio 비포함 조건을 추가
- Removed:
- none
#### Impacted Area Delta
- Docs:
- docs/batches/bat-001_20260502_trex-basic-v1-api-contract/verification.md: REQ-006 AC1 evidence를 owner scope 기준으로 재정렬
- Modules:
- app/api/v1_assets.py: `owner_user_id=auth.user_id` 적용으로 owner scope 강제
- Tests:
- tests/test_v1.py: `test_v1_assets_returns_asset_list`에 `owner_user_id="user-1"` 전달 assert 추가
#### Delivery Trace Delta
- Batches:
- BAT-001 (release-candidate) — REQ-006 targeted re-verification 필요 및 evidence 갱신
- Releases:
- REL-001 (draft) — release 입력 전 owner scope 기준 반영 여부 재확인 필요
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes
- Status Recommendation: keep-current
- Follow-up Action: owner scope 수정 코드와 targeted pytest 재실행 결과를 BAT-001 verification에 반영하고 Implemented 상태 유지
#### Validation Plan
- `app/api/v1_assets.py`에서 `owner_user_id=auth.user_id` 적용 여부 확인
- `tests/test_v1.py -k 'v1_assets'` 재실행으로 GET /v1/assets 회귀 테스트 PASS 확인
- BAT-001 verification.md의 REQ-006 AC1 evidence를 최신 테스트와 맞춤
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-001 re-verified verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-001 verification.md (re-verified, 2026-05-03, CHG-20260503-01 strategy_type 재검증 포함) AC-1~9 모두 PASS. Blocking Issues 없음.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-001 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-001)
#### Validation Plan
- BAT-001 verification.md (re-verified) AC-1~9 evidence 연결 확인 완료 (strategy_type 필드 재검증 포함)
### CHG-20260503-01
- Date: 2026-05-03
- Author: repository-maintainers
#### Change Summary
- GET /v1/assets 응답 필드에 `strategy_type` 추가 — StrategySet.name 값을 반환
#### Intent
- trex-basic 데몬이 asset 뷰에서 전략 이름(StrategySet.name)을 직접 확인할 수 있도록 `strategy_type` 필드를 응답에 추가한다.
- DB 변경 없이 기존 StrategySet.name 컬럼을 활용하는 순수 응답 스키마 확장이다.
#### Acceptance Criteria Delta
- Added:
- AC-8: Given portfolio에 active_strategy_id로 연결된 StrategySet이 있을 때, Then strategy_type은 StrategySet.name 값을 반환해야 한다.
- AC-9: Given portfolio에 active_strategy_id가 없거나 StrategySet 조회 실패 시, Then strategy_type은 null을 반환해야 한다.
- Modified:
- AC-3: `strategy_type` 필드를 필수 응답 필드 목록에 추가
#### Impacted Area Delta
- Modules:
- app/schemas/v1.py: AssetItem에 strategy_type 필드 추가
- app/api/v1_assets.py: strategy_type 매핑 로직 추가 (StrategySet.name 조회)
#### Delivery Trace Delta
- Batches:
- BAT-001 (release-candidate) — REQ-006 포함, strategy_type 추가로 재검증 필요
- Releases:
- 없음 (아직 draft-release 미수행)
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes (BAT-001 verification은 strategy_type 없이 수행됨)
- Status Recommendation: keep-current (Approved 유지 — 구현 전 단계이므로 상태 되돌림 불필요)
- Follow-up Action: 구현 후 BAT-001 verification 재수행 또는 BAT-006 신규 생성 결정 필요
#### Validation Plan
- app/schemas/v1.py AssetItem에 strategy_type 필드 추가
- app/api/v1_assets.py에서 portfolio.active_strategy_id로 StrategySet.name 조회 후 매핑
- tests/test_v1_assets.py 또는 test_app.py에서 strategy_type 필드 존재 및 null 케이스 검증
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-006 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 7개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### 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: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-006 최초 생성 (source Discovery: dcy-002 FR-3)
#### Intent
- trex-basic Consumer-Driven Contract의 GET /v1/assets 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md (REQ-011에서 갱신 예정)
- Modules:
- app/api/v1_assets.py 신규
- app/main.py (/v1 라우터 등록)
- Tests:
- tests/test_v1_assets.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-009 migration(market 컬럼 추가)과 함께 delivery 단위 구성 권장.
@@ -0,0 +1,82 @@
# REQ-007: pfhub tag CLI 명령어
- Status: Proposed
- Type: Interface
- Priority: Medium
- Owner: Justin Ko
## Intent
- tags 마스터 테이블 기반의 태그 관리 CLI 명령어를 `pfhub tag` 최상위 서브커맨드로 추가하여 운영자·사용자가 터미널에서 태그 생성, 조회, 비활성화, 적용, 해제를 수행할 수 있도록 한다.
- 기존 `pfhub portfolio tag add/list/remove` 명령어(portfolio_tags 자유 문자열 기반)는 이 REQ 구현과 함께 제거한다.
## Requirement
- `pfhub tag create <name>` — 태그 생성 (trade-write 이상 권한)
- `pfhub tag list [--inactive]` — 태그 목록; --inactive 없으면 활성 태그만, --inactive 플래그 시 비활성 태그 포함 전체 조회
- `pfhub tag deactivate <tag_id>` — 태그 비활성화; `--force` 확인 필요 (admin 또는 생성자 본인)
- `pfhub tag apply asset <asset_id> <tag_id>` — asset에 태그 적용 (admin 권한)
- `pfhub tag apply portfolio <portfolio_id> <tag_id>` — portfolio에 태그 적용 (trade-write 이상, 소유자 또는 admin)
- `pfhub tag remove asset <asset_id> <tag_id>` — asset에서 태그 해제; `--force` 확인 필요 (admin)
- `pfhub tag remove portfolio <portfolio_id> <tag_id>` — portfolio에서 태그 해제; `--force` 확인 필요 (소유자 또는 admin)
- 기존 `pfhub portfolio tag add`, `pfhub portfolio tag list`, `pfhub portfolio tag remove` 명령어를 제거해야 한다.
## Acceptance Criteria
1. Given trade-write 토큰, When `pfhub tag create "코스피200"`, Then 태그가 생성되고 `pfhub tag list`에 나타난다.
2. Given 활성 태그 존재, When `pfhub tag list`, Then 활성 태그 목록이 출력된다.
3. Given 활성·비활성 태그 모두 존재, When `pfhub tag list --inactive`, Then 비활성 태그가 포함된 전체 목록이 출력된다.
4. Given admin 또는 생성자 토큰, When `pfhub tag deactivate <tag_id> --force`, Then 태그가 비활성화되고 `pfhub tag list --inactive`에서 비활성 상태로 확인된다.
5. Given admin 토큰, admin 생성 활성 tag_id, When `pfhub tag apply asset <asset_id> <tag_id>`, Then asset에 태그가 적용된다.
6. Given trade-write 토큰(소유자 또는 admin), When `pfhub tag apply portfolio <portfolio_id> <tag_id>` (정책 충족 태그), Then portfolio에 태그가 적용된다.
7. Given admin 토큰, When `pfhub tag remove asset <asset_id> <tag_id> --force`, Then asset에서 태그가 제거된다.
8. Given 소유자 또는 admin 토큰, When `pfhub tag remove portfolio <portfolio_id> <tag_id> --force`, Then portfolio에서 태그가 제거된다.
9. Given 기존 CLI 실행 환경, When `pfhub portfolio tag add/list/remove`를 실행, Then 명령어가 존재하지 않는다(Typer error 또는 명령 없음).
## Impacted Area
- Docs: docs/project-structure.md (pfhub tag 커맨드 그룹 추가, pfhub portfolio tag 제거), docs/runtime-flows.md (tag CLI flow 추가)
- Modules:
- app/cli/commands/ — tag.py 신규 파일 (pfhub tag 커맨드 그룹)
- app/cli/main.py — app.add_typer(tag_app, name="tag") 추가
- app/cli/commands/portfolio.py — tag_app, tag_add, tag_list, tag_remove 제거
- app/services/ — 태그 CLI 서비스 메서드 (create, list, deactivate, apply, remove) 추가
- Tests: pfhub tag 커맨드 각 AC 기능 테스트, pfhub portfolio tag 제거 확인
## Notes
- runtime-flows.md의 CLI 흐름 제약 준수: AppContext 통해 settings와 repository 주입.
- `pfhub tag apply`/`remove`는 REST API(REQ-005)를 통해 정책 검증이 이루어지므로 CLI는 HTTP 오류를 그대로 사용자에게 표시.
- REQ-004, REQ-005가 선행 구현되어야 한다.
- Source: dcy-002 FR-6
## Change Log
### CHG-20260519-01
- Date: 2026-05-19
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
#### Intent
- dcy-002 FR-6를 Interface REQ로 분리; 기존 pfhub portfolio tag 명령어 교체 명시
#### Acceptance Criteria Delta
- Added:
- AC-1~9 최초 작성
#### Impacted Area Delta
- Docs:
- docs/project-structure.md, docs/runtime-flows.md — 최초 지정
- Modules:
- app/cli/commands/tag.py (신규), app/cli/main.py, app/cli/commands/portfolio.py, app/services/ — 최초 지정
- 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-004, REQ-005 선행 의존성 확인
@@ -1,177 +0,0 @@
# req-007: GET /v1/user_info 엔드포인트 구현
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic 데몬이 사용자 거래 가능 여부와 브로커 인증 정보를 조회할 수 있도록 한다.
- `runtime_id`는 인증 service-token의 런타임 세대 식별자이고, `daemon_id`는 stable logical identity로 함께 반환되어야 한다.
- credential_payload는 DB에서 Fernet 복호화 후 반환한다(NFR-3/REQ-010 의존).
## Requirement
- GET /v1/user_info는 service-token bearer 인증으로 `runtime_id`, `daemon_id`, `trading_enabled`(users.is_active 매핑), `disabled_reason`, `brokers[]`(broker, account_ref, credential_payload, credential_version, last_rotated_at)를 반환해야 한다.
- environment 필드를 응답에 포함하지 않는다.
- credential_payload는 Fernet으로 복호화된 JSON을 반환한다.
- transition 구간의 legacy token은 `daemon_id = null`을 진단용으로 반환할 수 있어야 하며, `runtime_id`가 없는 구형 token 처리 규칙은 기존 v1 contract를 유지한다.
## Acceptance Criteria
1. Given `runtime_id`가 연결된 service-token으로 `GET /v1/user_info` 호출 시, Then HTTP 200과 응답 JSON에 `runtime_id`, `daemon_id`, `trading_enabled`, `brokers[]` 필드가 포함되어야 한다.
2. Given GET /v1/user_info 응답을 확인하면, Then environment 필드가 존재하지 않아야 한다.
3. Given users.is_active가 true인 사용자의 service-token으로 호출 시, Then 응답의 trading_enabled가 true여야 한다.
4. Given users.is_active가 false인 사용자의 service-token으로 호출 시, Then 응답의 trading_enabled가 false여야 한다.
5. Given broker_credentials에 KIS credential이 저장된 사용자의 토큰으로 호출 시, When 응답의 brokers[] 항목을 확인하면, Then credential_payload가 복호화된 JSON({app_key, app_secret})으로 반환되어야 한다.
6. Given runtime_id가 없는 구형 service-token으로 호출 시, Then 설계 단계에서 확정된 오류 응답이 반환되어야 한다(R-4, 404 또는 즉시 runtime_id 생성 중 선택).
7. Given 인증 없이 요청 시, Then HTTP 401이 반환되어야 한다.
8. Given `daemon_id = NULL`로 backfill된 legacy service-token으로 호출 시, Then HTTP 200 응답이 유지되고 `daemon_id` 필드는 `null`로 노출되어야 한다.
## Impacted Area
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/data-model.md (REQ-011에서 갱신 예정)
- Modules: app/api/v1_user_info.py, app/schemas/v1.py, app/core/auth.py, app/domain/models.py, app/services/, app/repositories/postgres.py, app/main.py
- Tests: tests/test_v1.py, tests/test_auth.py
## Notes
- trading_enabled는 users.is_active 단순 매핑이다(A-4 Confirmed). 별도 trading 전용 상태 필드는 이번 반복 Out of Scope.
- disabled_reason: trading_enabled가 false일 때 이유 문자열. 이번 반복에서는 is_active가 false인 경우 "account_inactive" 고정값을 사용하는 방안을 설계 단계에서 확정한다.
- `daemon_id = NULL` legacy token은 transition diagnostics 대상으로 허용되지만, daemon-scoped read/write endpoint에서는 fail-closed가 적용된다.
- R-4(기존 service-token에 runtime_id 없는 경우 처리): 404 또는 즉시 생성 중 설계 단계에서 결정. 이 REQ의 Acceptance Criteria 6번은 runtime_id 부재 처리 규칙에 한해 유지한다.
- Fernet 복호화는 REQ-010에서 정의한 암호화 유틸리티를 사용한다.
- Source Discovery: dcy-002 (FR-4, NFR-1, A-4), dcy-006 (FR-1, FR-5, NFR-1)
## Change Log
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- `/v1/user_info` 응답 요구사항에 stable `daemon_id` diagnostics를 추가하고, runtime_id-only 구현 증거를 무효화하여 Status를 Approved로 되돌림
#### Intent
- dcy-006은 `/v1/user_info`를 runtime 세대 식별과 daemon binding diagnostics를 함께 제공하는 endpoint로 재정의한다. 현재 구현/verification은 `runtime_id`만 기준으로 삼아 revised contract를 충족하지 못한다.
#### Acceptance Criteria Delta
- Added:
- AC-8: legacy token의 `daemon_id = null` diagnostics
- Updated:
- AC-1: 응답 필수 필드에 `daemon_id` 추가
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `docs/interface-contract.md`, `docs/data-model.md`, `docs/runtime-flows.md`
- Modules:
- `app/api/v1_user_info.py`, `app/schemas/v1.py`, `app/core/auth.py`, `app/repositories/postgres.py`
- Tests:
- `tests/test_v1.py`, `tests/test_auth.py`
#### Delivery Trace Delta
- Batches:
- BAT-001 (release-candidate; REL-001 released input) — `/v1/user_info` evidence를 daemon diagnostics 기준으로 재검증해야 함
- Releases:
- REL-001 (released) — released smoke는 `runtime_id`만 확인했으므로 revised REQ 충족 증거로 재사용 불가
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes
- Status Recommendation: rollback-to-approved
- Follow-up Action: dcy-006 후속 batch에서 `daemon_id` schema/API contract와 legacy diagnostic behavior를 구현한다.
#### Validation Plan
- `app/api/v1_user_info.py`, `app/schemas/v1.py` 현재 구현에 `daemon_id`가 없는지 코드 리뷰로 확인한다.
- 구현 후 daemon-bound token, legacy daemon-less token, runtime_id-missing token 시나리오를 분리해 targeted pytest를 추가한다.
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-001 re-verified verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-001 verification.md (re-verified, 2026-05-03) AC-1~7 모두 PASS. Blocking Issues 없음.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-001 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-001)
#### Validation Plan
- BAT-001 verification.md (re-verified) AC-1~7 evidence 연결 확인 완료
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-007 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 7개, Impacted Area, Change Log) 모두 충족 확인 후 승인
- 설계 의존성 주의: AC #6(runtime_id 없는 구형 토큰 R-4 처리: 404 또는 즉시 생성)은 설계 단계에서 확정 필요. 핵심 Requirement가 명확하고 Notes에 명시적으로 deferred 처리됨 — 승인 게이트 통과 판단.
#### 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: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch. 설계 단계에서 R-4 처리 방식 확정 후 AC #6 구체화.
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-007 최초 생성 (source Discovery: dcy-002 FR-4)
#### Intent
- trex-basic Consumer-Driven Contract의 GET /v1/user_info 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md (REQ-011에서 갱신 예정)
- Modules:
- app/api/v1_user_info.py 신규
- app/domain/models.py BrokerCredential 추가
- Tests:
- tests/test_v1_user_info.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-009(broker_credentials 테이블), REQ-010(Fernet 암호화), REQ-005(runtime_id)와 함께 delivery 단위 구성 권장.
@@ -1,213 +0,0 @@
# req-008: POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 구현
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic이 체결 내역을 portfolio-hub에 idempotent하게 전송할 수 있도록 한다.
- 동일 (asset_id, order_id, execution_id) 조합의 중복 수신은 DB unique constraint로 감지하고 status=duplicate를 반환한다(NFR-2).
- 동일 키에 다른 payload가 오면 충돌로 처리하며, daemon assignment mismatch 또는 미배정 asset write는 저장 전에 거부한다.
## Requirement
- POST /v1/assets/{asset_id}/ledger/trades는 service-token bearer 인증으로, 인증된 token의 `daemon_id`에 배정된 asset에 대해서만 체결 내역(execution_id, order_id, side, quantity, price, fee_amount, tax_amount, executed_at)을 ledger_trades 테이블에 저장하고 HTTP 201을 반환해야 한다.
- 동일 (asset_id, order_id, execution_id) 재수신은 신규 행 생성 없이 status=duplicate와 기존 ledger_entry_id를 HTTP 200으로 반환해야 한다.
- 동일 키에 payload가 다른 경우 HTTP 409를 반환해야 한다.
- idempotency는 DB unique constraint로 보장하며, 애플리케이션 로직만으로 보장하지 않는다.
- daemon assignment mismatch, 미배정 asset, `daemon_id = NULL` legacy token은 모두 HTTP 403으로 fail-closed 처리해야 하며 ledger row가 생성되면 안 된다.
## Acceptance Criteria
1. Given 유효한 daemon-bound service-token과 해당 daemon에 배정된 asset의 신규 체결 데이터로 POST /v1/assets/{asset_id}/ledger/trades 요청 시, Then HTTP 201과 생성된 ledger_entry_id가 반환되어야 한다.
2. Given 동일 (asset_id, order_id, execution_id)와 동일 payload로 두 번째 요청 시, Then HTTP 200과 status="duplicate", 기존 ledger_entry_id가 반환되어야 하며 DB에 단일 행만 존재해야 한다.
3. Given 동일 (asset_id, order_id, execution_id)에 다른 payload로 요청 시, Then HTTP 409가 반환되어야 한다.
4. Given asset_id가 존재하지 않는 portfolio_id인 경우, Then HTTP 404가 반환되어야 한다.
5. Given 인증 없이 요청 시, Then HTTP 401이 반환되어야 한다.
6. Given asset이 다른 daemon에 배정되어 있는 경우, When 요청 daemon이 asset owner daemon과 다르면, Then HTTP 403이 반환되어야 하고 ledger row는 생성되지 않아야 한다.
7. Given asset이 아직 daemon에 배정되지 않았거나 토큰의 `daemon_id`가 NULL인 경우, Then HTTP 403이 반환되어야 하고 ledger row는 생성되지 않아야 한다.
## Impacted Area
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/data-model.md (REQ-011에서 갱신 예정)
- Modules: app/api/v1_ledger.py, app/domain/models.py, app/services/, app/schemas/, app/repositories/portfolio_repository.py, app/repositories/postgres.py, app/main.py
- Tests: tests/test_v1.py 또는 신규 daemon assignment / ledger authorization 회귀 테스트
## Notes
- NFR-2(idempotency DB unique constraint): (asset_id, order_id, execution_id) unique constraint는 REQ-009 migration에서 정의한다.
- ledger_trades 테이블과 기존 portfolio_ledger 테이블은 별개다. portfolio_ledger는 내부 원장이고 ledger_trades는 trex-basic이 외부에서 기록하는 체결 내역이다.
- 중복 감지 구현: DB unique constraint 위반(psycopg UniqueViolation) 캐치 → 기존 행 조회 → payload 동일 여부 비교 → duplicate 또는 409 응답.
- daemon authorization은 기존 permission(`trade-write`) 위에 추가되는 두 번째 authorization 축이다.
- Source Discovery: dcy-002 (FR-5, NFR-2), dcy-006 (FR-2, FR-4, NFR-2)
## Change Log
### CHG-20260518-01
- Date: 2026-05-18
- Author: repository-maintainers
#### Change Summary
- `insert_ledger_trade()` 미구현 결함 수정 — PostgresPortfolioRepository에 메서드가 존재하지 않아 모든 POST /v1/assets/{asset_id}/ledger/trades 요청이 HTTP 500으로 실패하던 결함을 수정. Status Approved 유지.
#### Intent
- `app/api/v1_ledger.py:78`에서 `repo.insert_ledger_trade()`를 호출하지만 PostgresPortfolioRepository에 구현이 없어 AttributeError → HTTP 500이 발생했다. FakeRepo 기반 테스트는 통과했으나 실 DB 환경에서는 endpoint 전체가 불동작 상태였다. trex-basic의 write-back이 전면 차단된 High 심각도 결함이었으며, 구현을 추가해 정상화했다.
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- Modules:
- `app/repositories/postgres.py`: `insert_ledger_trade(asset_id, order_id, execution_id, side, quantity, price, executed_at, fee_amount, tax_amount)` 추가. `INSERT INTO ledger_trades ... ON CONFLICT ON CONSTRAINT uq_ledger_trades_idempotency DO NOTHING RETURNING *` 패턴으로 신규/중복 여부를 `(LedgerTrade, bool)` 튜플로 반환.
- `app/repositories/portfolio_repository.py`: Protocol에 `insert_ledger_trade` 선언 추가, `LedgerTrade` import 추가
- Tests: 기존 FakeRepo 기반 테스트는 영향 없음. 실 DB 통합 테스트(AC-1~7)는 별도 재검증 필요.
#### Delivery Trace Delta
- Batches: BAT-010 (release-candidate) — 결함 수정이 BAT-010 delivery scope에 포함
- Releases: REL-006 (draft) — 배포 전 결함 수정 완료. 실 DB 통합 재검증 후 배포 권장.
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: no (AC 정의는 유효, postgres 구현만 누락되어 있었음)
- Status Recommendation: keep-current (Approved; CHG-20260513-01에 의해 Approved 상태)
- Follow-up Action: 실 DB 환경에서 AC-1(신규 체결 201), AC-2(중복 200+duplicate), AC-3(payload 불일치 409), AC-6~7(daemon mismatch/미배정 403) 통합 시나리오 검증 후 Implemented 전환 가능
#### Validation Plan
- `POST /v1/assets/{asset_id}/ledger/trades`에 유효 daemon-bound service-token으로 요청 시 HTTP 201 반환 확인
- 동일 (asset_id, order_id, execution_id) 재요청 시 HTTP 200 + status=duplicate 확인
- daemon mismatch, 미배정 asset 요청 시 HTTP 403 확인
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- ledger write REQ에 daemon assignment authorization과 403 fail-closed 규칙을 추가하고, 기존 Implemented 상태를 Approved로 되돌림
#### Intent
- dcy-006은 `trade-write` permission만으로는 충분하지 않고, asset이 인증 daemon에 배정됐는지까지 검증하도록 요구한다. 현재 구현은 asset 존재성만 확인하므로 revised authorization contract를 충족하지 못한다.
#### Acceptance Criteria Delta
- Added:
- AC-6: daemon mismatch 403
- AC-7: 미배정 asset 또는 daemon-less token 403
- Updated:
- AC-1: 성공 조건을 daemon-assigned asset으로 제한
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/data-model.md`
- Modules:
- `app/api/v1_ledger.py`, `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`
- Tests:
- `tests/test_v1.py` 또는 신규 daemon assignment ledger authorization 테스트
#### Delivery Trace Delta
- Batches:
- BAT-001 (release-candidate; REL-001 released input) — ledger verification evidence는 404/409/idempotency 중심이라 daemon authz 케이스를 추가해 재검증해야 함
- Releases:
- REL-001 (released) — released trade-write contract는 daemon mismatch 403 정책을 포함하지 않음
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes
- Status Recommendation: rollback-to-approved
- Follow-up Action: dcy-006 후속 batch에서 asset assignment lookup과 ledger authorization guard를 구현하고 duplicate/conflict 회귀와 함께 재검증한다.
#### Validation Plan
- `app/api/v1_ledger.py`가 현재 asset 존재성만 확인하는지 코드 리뷰로 확인한다.
- 구현 후 success/duplicate/conflict/404와 daemon mismatch·unassigned 403 시나리오를 함께 검증한다.
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-001 re-verified verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-001 verification.md (re-verified, 2026-05-03) AC-1~5 모두 PASS. Blocking Issues 없음.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-001 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-001)
#### Validation Plan
- BAT-001 verification.md (re-verified) AC-1~5 evidence 연결 확인 완료
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-008 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 5개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### 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: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-008 최초 생성 (source Discovery: dcy-002 FR-5)
#### Intent
- trex-basic Consumer-Driven Contract의 POST /v1/assets/{asset_id}/ledger/trades 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md (REQ-011에서 갱신 예정)
- Modules:
- app/api/v1_ledger.py 신규
- app/domain/models.py LedgerTrade 추가
- Tests:
- tests/test_v1_ledger.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-009(ledger_trades 테이블 + unique constraint)와 함께 delivery 단위 구성 필수.
@@ -1,211 +0,0 @@
# req-012: service-token rotate CLI 명령 구현
- Status: Approved
- Type: Interface
- Priority: Medium
- Owner: repository-maintainers
## Intent
- 발급된 service-token의 원문이 노출되었거나 주기적 교체가 필요한 경우, token을 revoke 없이 in-place로 갱신할 수 있는 CLI 명령이 필요하다.
- 기존 `revoke → re-issue` 방식은 `token_id`, `user_id`, `permission`, `expires_at`, `created_at` 등 메타데이터가 변경되므로, 운영 시스템에서 동일한 이름으로 참조하는 통합 구성을 다시 설정해야 하는 불편이 있다.
- rotate는 `token_hash`, `token_prefix`, `runtime_id`만 교체하고 `daemon_id`를 포함한 나머지 메타데이터를 유지하여, 동일한 token 이름과 daemon binding을 유지한 채 새 token 원문만 배포할 수 있게 한다. 단, `runtime_id`를 소비하는 시스템은 rotate 후 새 토큰 기준으로 runtime_id를 재조회해야 한다.
## Requirement
- `pfhub service-token rotate <name> [--force]` CLI 명령을 구현한다.
- rotate는 기존 auth_token 행의 `token_hash`, `token_prefix`, `runtime_id`를 새 값으로 UPDATE하고, `daemon_id`는 유지해야 한다.
- `id`, `user_id`, `name`, `permission`, `token_type`, `expires_at`, `created_at`, `daemon_id`는 변경하지 않는다.
- rotate 대상 token이 존재하지 않으면 오류를 반환한다.
- rotate 대상 token이 이미 revoked 상태이면 오류를 반환한다.
- rotate 대상 token의 `expires_at`이 NULL이 아닌 경우(만료일이 설정된 토큰) 오류를 반환한다. rotate는 `--no-expiry`로 발급된 영구 토큰에만 허용된다.
- `--force` 없이 실행하면 확인 프롬프트를 표시한다.
- rotate 성공 시 새 token 원문을 1회 출력하고, 이후 list/조회에서는 token_prefix만 표시된다. rotate 출력 메타데이터에는 `daemon_id`가 포함되어야 한다.
## Acceptance Criteria
1. Given `expires_at IS NULL`인 유효한 service-token 이름, When `pfhub service-token rotate <name> --force`를 실행, Then DB 행의 `token_hash`, `token_prefix`, `runtime_id`가 새 값으로 변경되고 `id`, `permission`, `expires_at`(NULL), `daemon_id`는 기존과 동일하다.
2. Given rotate 성공, When CLI 출력, Then 새 token 원문이 화면에 1회 출력되고 "이 토큰은 지금 1회만 표시" 안내 메시지와 함께 `daemon_id` 메타데이터가 표시되어야 한다.
3. Given 존재하지 않는 token 이름, When `rotate`, Then 오류 메시지를 출력하고 종료 코드 1을 반환한다.
4. Given revoked 상태의 token, When `rotate`, Then 오류 메시지를 출력하고 종료 코드 1을 반환한다.
5. Given `expires_at`이 NULL이 아닌(만료일이 설정된) token, When `rotate`, Then "만료일이 설정된 토큰은 rotate할 수 없습니다" 오류를 출력하고 종료 코드 1을 반환한다.
6. Given `--force` 없이 실행, When CLI, Then 확인 프롬프트를 표시하고 거절 시 변경 없이 종료한다.
## Impacted Area
- Docs: docs/project-structure.md (변경 없음), docs/runtime-flows.md (변경 없음)
- Modules:
- `app/repositories/portfolio_repository.py` — `rotate_service_token` Protocol stub 추가
- `app/repositories/postgres.py` — `rotate_service_token` UPDATE 구현 추가
- `app/services/auth_service.py` — `rotate_service_token(name)` 서비스 메서드 추가
- `app/cli/commands/service_tokens.py` — `rotate` 명령 및 `daemon_id` 출력 추가
- `tests/test_cli.py` — stub repo에 `rotate_service_token` 추가 및 테스트 케이스 추가
- Tests:
- `tests/test_cli.py`: rotate 성공, 없는 이름, revoked 상태, `daemon_id` 유지/출력 케이스
## Notes
- `expires_at`은 rotate 시 변경하지 않는다. 만료일 변경이 필요하면 revoke + re-issue를 사용한다.
- rotate는 `--no-expiry`로 발급된 영구 토큰(`expires_at IS NULL`)에만 허용된다. 만료일이 설정된 토큰은 rotate 대상이 아니다. 해당 토큰은 만료 후 re-issue하는 것이 권장 패턴이다.
- rotate는 `runtime_id`도 새 값으로 갱신하지만 `daemon_id`는 유지한다. trex-basic 등 `runtime_id`를 소비하는 시스템은 rotate 후 새 토큰으로 다시 인증하고 `GET /v1/user_info`를 재호출해 새 `runtime_id`를 재인식해야 한다.
- 기존 `runtime_id`를 캐싱한 상태로 계속 사용하면 consumer 식별 불일치가 발생할 수 있다. rotate는 "토큰 원문 교체 시 token name과 메타데이터를 유지한다"는 의미이지, `runtime_id`까지 불변이라는 뜻은 아니다.
- rotate는 daemon provisioning 경로가 아니며, unknown daemon 자동 생성/업서트를 수행하면 안 된다.
- DB 스키마 변경(migration)은 없다. 기존 `auth_tokens` 테이블의 기존 컬럼만 UPDATE한다.
- 구조/인터페이스/런타임 흐름 변경 없음 → minor-change fast path 적용.
## Change Log
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- rotate REQ에 `daemon_id` 불변식과 CLI diagnostic 출력 요구를 추가하고, 기존 Implemented 상태를 Approved로 되돌림
#### Intent
- dcy-006은 rotate 이후에도 `daemon_id`가 stable identity로 유지되어야 하고, 운영자는 rotate 출력과 provenance 절차에서 `daemon_id`를 확인할 수 있어야 한다. BAT-002/REL-002 구현은 runtime_id lifecycle만 다루므로 revised REQ를 충족하지 못한다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- AC-1: `daemon_id` 유지 불변식 추가
- AC-2: CLI output에 `daemon_id` metadata 추가
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `README.md`, token provenance 문서, `docs/runtime-flows.md`
- Modules:
- `app/cli/commands/service_tokens.py`, `app/services/auth_service.py`, `app/repositories/postgres.py`
- Tests:
- `tests/test_cli.py`, 필요한 경우 auth/v1 provenance 회귀 테스트
#### Delivery Trace Delta
- Batches:
- BAT-002 (release-candidate; REL-002 released input) — rotate verification evidence는 `daemon_id` 유지/출력 기준으로 재검증 필요
- Releases:
- REL-002 (released) — rotate 운영 메모는 runtime_id 재조회만 다루며 daemon-aware provenance는 미반영
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes
- Status Recommendation: rollback-to-approved
- Follow-up Action: dcy-006 후속 batch에서 rotate response metadata와 daemon binding invariants를 구현하고, REL-002 후속 tooling release evidence를 갱신한다.
#### Validation Plan
- current CLI/service/repository 구현이 `daemon_id`를 보존·출력하지 않는지 코드 리뷰로 확인한다.
- 구현 후 rotate 전후 `daemon_id` 불변, `runtime_id` 갱신, CLI output metadata를 targeted CLI 테스트로 검증한다.
### CHG-20260511-02
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-002 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-002 verification.md에서 REQ-012 AC-1~6이 모두 PASS이고 blocking issue가 없음을 근거로 Implemented로 승격한다.
#### Acceptance Criteria Delta
- Added: none
- Updated: none
- Removed: none
#### Impacted Area Delta
- Docs: none
- Modules: none
- Tests: none
#### Delivery Trace Delta
- Batches:
- BAT-002 (release-candidate)
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: tooling release에 포함 시 rotate 후 runtime_id 재조회 운영 메모 반영
#### Validation Plan
- BAT-002 verification.md의 REQ-012 AC-1~6 evidence와 Ready for Release: true 확인
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- rotate 후 `runtime_id` lifecycle 명시 추가 — downstream consumer는 새 토큰 배포 후 `runtime_id`를 재조회해야 함
#### Intent
- CONFLICT-3 분석 결과를 반영해 `runtime_id`가 rotate 시 변경된다는 사실과, trex-basic 같은 consumer가 이를 어떻게 재인식해야 하는지 REQ-012 문서에 명시한다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `docs/srs/Interface/req-012_service-token-rotate-cli.md`: Intent/Notes에 rotate 후 `runtime_id` 재조회 lifecycle 추가
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- BAT-002 (release-candidate) — verification evidence는 유지 가능하나, rotate 후 consumer 재인식 절차를 운영 메모로 함께 검토해야 한다.
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: trex-basic 연동 runbook 또는 후속 REQ에서 rotate 후 `runtime_id` 재조회 절차를 운영 문서에 반영 검토
#### Validation Plan
- REQ-005, REQ-007, REQ-012 문구를 대조해 rotate 시 `runtime_id` 변경과 `GET /v1/user_info` 재조회 필요성이 일관되게 설명되는지 코드 리뷰로 확인
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-012 신규 작성 (minor-change fast path, Approved 직행)
- rotate 허용 대상을 `expires_at IS NULL`(no-expiry) 토큰으로 제한
#### Intent
- service-token rotate CLI 명령 필요성 확인 후 즉시 구현 진행
#### Acceptance Criteria Delta
- Added:
- 6개 기준 신규 정의 (AC-5: expires_at 설정 토큰 rotate 거부 추가)
#### Impacted Area Delta
- Modules:
- postgres.py, auth_service.py, service_tokens.py, test_cli.py 추가
- Tests:
- test_cli.py 3개 케이스 신규
#### Delivery Trace Delta
- Batches:
- BAT-002 (batch-lite)
- Releases:
- none (미정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: BAT-002 planning → implementation → verification 진행
#### Validation Plan
- test_cli.py 3개 케이스 통과 + 기존 service-token 테스트 회귀 없음으로 완료 판단
@@ -1,143 +0,0 @@
# REQ-013: CLI `portfolio list --user` 필수화 및 소유자 기반 조회
- Status: Implemented
- Type: Interface
- Priority: Medium
- Owner: repository-maintainers
## Intent
- CLI `pfhub portfolio list`는 현재 `--user` 옵션이 없어 `owner_user_id=None`으로 호출되며 전체 포트폴리오를 반환한다. 이 동작은 소유자 구분 없이 DB 전체를 노출하는 문제가 있다.
- `--user <username>` 옵션을 필수로 지정하여 CLI 실행 시 항상 특정 사용자의 포트폴리오만 조회하도록 강제한다.
- username을 `owner_user_id`(UUID)로 변환하는 로직을 CLI 내부에 추가하여, `PortfolioService.list_portfolios` 호출 시 소유자 필터가 항상 적용되게 한다.
## Requirement
- CLI `pfhub portfolio list` 명령은 `--user <username>` 옵션을 필수로 받아야 한다.
- `--user` 없이 실행하면 오류 메시지를 출력하고 exit code 1로 종료해야 한다.
- `--user`로 전달된 username을 user repository(`get_user_by_username`)로 조회하여 `owner_user_id`(UUID)를 얻고, 이를 `PortfolioService.list_portfolios(owner_user_id=...)` 호출에 전달해야 한다.
- 존재하지 않는 username이 전달되면 기존 CLI 오류 출력 형식(Rich Console 또는 typer.echo)과 일관된 오류 메시지를 출력하고 비정상 종료해야 한다.
## Acceptance Criteria
1. Given `--user` 옵션 없이 `pfhub portfolio list`를 실행할 때, When 명령이 실행되면, Then 오류 메시지("--user 옵션이 필요합니다" 또는 동등한 내용)가 출력되고 exit code 1로 종료된다.
2. Given 유효한 username `alice`로 `pfhub portfolio list --user alice`를 실행할 때, When 명령이 실행되면, Then `alice`의 `owner_user_id`로 필터된 포트폴리오 목록만 반환되고 다른 사용자 소유 포트폴리오는 포함되지 않는다.
3. Given 존재하지 않는 username으로 `pfhub portfolio list --user unknown`을 실행할 때, When 명령이 실행되면, Then "사용자를 찾을 수 없습니다" 형태의 오류 메시지가 출력되고 exit code 1로 종료된다.
4. Given in-memory 모드(`pfhub_DB_ENABLED=false`)에서 `pfhub portfolio list --user alice`를 실행할 때, When 명령이 실행되면, Then DB 모드와 동일하게 `alice` 소유 포트폴리오만 반환된다.
5. Given 오류 발생 시, When 오류 메시지가 출력될 때, Then 메시지 형식은 기존 CLI 오류 출력 형식(Rich Console stderr 또는 typer.echo)과 일관성을 유지한다.
## Impacted Area
- Docs:
- `README.md` — `pfhub portfolio list` 예제 라인에 `--user <username>` 옵션 추가 및 breaking change 안내
- Modules:
- `app/cli/commands/portfolio.py` — `list_portfolios` 명령 시그니처에 `--user` 필수 옵션 추가, username → owner_user_id 변환 로직 추가, `PortfolioService.list_portfolios` 호출 시 `owner_user_id` 전달
- `app/repositories/portfolio_repository.py` — `get_user_by_username` Protocol stub 기존 존재 확인(line 16), 추가 변경 불필요
- `app/repositories/postgres.py` — `get_user_by_username` 기존 구현 확인(line 419), 추가 변경 불필요
- Tests:
- `tests/test_cli.py` — AC 1~5 커버 테스트 케이스 추가:
- `test_cli_portfolio_list_without_user_exits_error`
- `test_cli_portfolio_list_with_valid_user_filters_by_owner`
- `test_cli_portfolio_list_with_unknown_user_exits_error`
- `test_cli_portfolio_list_in_memory_mode_works`
## Notes
- `PortfolioService.list_portfolios` 시그니처는 변경하지 않는다. CLI 호출부에서만 `owner_user_id`를 전달한다 (A-3, dcy-003).
- HTTP API `GET /portfolios`의 접근 통제는 이번 REQ 범위 밖이다 (Out of Scope).
- `--all` 관리자 전체 조회 옵션은 이번 REQ에 포함하지 않는다. 후속 Discovery로 분리한다.
- username → owner_user_id 변환은 `repository.get_user_by_username(username)` 직접 호출로 구현한다. `UserService`에는 해당 메서드가 없음을 확인하였다.
- 기존 `_with_service` 헬퍼는 `PortfolioService`만 생성한다. username 조회를 위해 별도로 repository를 직접 사용하거나 `_with_service` callback 안에서 repository 접근이 필요하다. 구현 시 `app_context.repository_factory(settings)` 반환 객체를 통해 `get_user_by_username`을 호출하는 방식으로 처리한다.
- Source Discovery: [dcy-003](../../discovery/dcy-003_20260502_portfolio-list-user-required.md)
## Change Log
### CHG-20260502-03
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- Status Approved → Implemented. BAT-003(release-candidate) verification evidence 확인 완료.
#### Intent
- confirm-req-implemented 실행. AC 1~5 전원 evidence 연결 확인, Blocking Issues 없음.
#### Evidence Batch
- Batch: BAT-003 (release-candidate)
- Verification: docs/batches/bat-003_20260502_portfolio-list-user-required/verification.md
- Test result: 41 passed (pytest tests/test_cli.py)
#### Acceptance Criteria Delta
- 없음
#### Impacted Area Delta
- Docs: 없음
- Modules: 없음
- Tests: 없음
#### Delivery Trace Delta
- Batches: BAT-003 (release-candidate)
- Releases: none (draft-release 대기 중)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: draft-release
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- Status Proposed → Approved. 승인 게이트 통과. 필수 필드, AC 5개, Impacted Area, Change Log 모두 충족.
#### Intent
- confirm-req 실행으로 구현 delivery 입력 준비 확정.
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- 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: batch 생성 진행 (draft-batch)
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-013 최초 생성 (Proposed). dcy-003 FR-1, FR-2, FR-3 통합. NFR-1, NFR-2는 AC 5에 흡수.
#### Intent
- dcy-003 확정 Discovery를 REQ backlog로 정규화.
#### Acceptance Criteria Delta
- Added:
- AC 1~5 신규 작성
#### Impacted Area Delta
- Docs:
- README.md breaking change 안내 추가 항목
- Modules:
- app/cli/commands/portfolio.py 신규 변경
- Tests:
- tests/test_cli.py 4개 테스트 케이스 신규 추가
#### Delivery Trace Delta
- Batches: none (batch 미생성)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 실행 후 batch 계획 수립
@@ -1,78 +0,0 @@
# REQ-014: CLI `portfolio create --user` 필수화 및 소유자 설정
- Status: Implemented
- Type: Interface
- Priority: Medium
- Owner: repository-maintainers
## Intent
- CLI `pfhub portfolio create`는 현재 `--user` 옵션이 없어 `owner_user_id="system"` 고정값으로 포트폴리오가 생성된다.
- 생성 시 `--user <username>` 을 필수로 받아 실제 소유자의 `owner_user_id`(UUID)로 포트폴리오가 생성되도록 강제한다.
- REQ-013(`portfolio list --user` 필수화)과 대칭을 이루는 변경으로, 소유자 기반 포트폴리오 관리 정책을 CLI 전반에 일관되게 적용한다.
## Requirement
- CLI `pfhub portfolio create` 명령은 `--user <username>` 옵션을 필수로 받아야 한다.
- `--user` 없이 실행하면 오류 메시지를 출력하고 exit code 1로 종료해야 한다.
- `--user`로 전달된 username을 `repository.get_user_by_username(username)`으로 조회하여 `owner_user_id`(UUID)를 얻고, 이를 `PortfolioService.create_portfolio(owner_user_id=...)` 호출에 전달해야 한다.
- 존재하지 않는 username이 전달되면 기존 CLI 오류 출력 형식과 일관된 오류 메시지를 출력하고 비정상 종료해야 한다.
## Acceptance Criteria
1. Given `--user` 옵션 없이 `pfhub portfolio create <id> --symbol X --force`를 실행할 때, When 명령이 실행되면, Then 오류 메시지가 출력되고 exit code 1로 종료된다.
2. Given 유효한 username `alice`로 `pfhub portfolio create <id> --symbol X --user alice --force`를 실행할 때, When 명령이 실행되면, Then 생성된 포트폴리오의 `owner_user_id`가 alice의 UUID로 설정된다.
3. Given 존재하지 않는 username으로 실행할 때, When 명령이 실행되면, Then "사용자를 찾을 수 없습니다" 형태의 오류 메시지가 출력되고 exit code 1로 종료된다.
4. Given in-memory 모드에서 `--user alice`로 실행할 때, When 명령이 실행되면, Then DB 모드와 동일하게 alice의 UUID가 `owner_user_id`로 설정된다.
5. Given 오류 발생 시, When 오류 메시지가 출력될 때, Then 메시지 형식은 기존 CLI 오류 출력 형식(Rich Console)과 일관성을 유지한다.
## Impacted Area
- Docs:
- `README.md` — `pfhub portfolio create` 예제 라인에 `--user <username>` 옵션 추가
- Modules:
- `app/cli/commands/portfolio.py` — `create_portfolio` 명령 시그니처에 `--user` 필수 옵션 추가, `_with_service_and_repo` 콜백 사용, `owner_user_id` 전달
- Tests:
- `tests/test_cli.py` — 기존 `test_cli_create_buy_and_delete_flow` 수정 + AC 1~4 커버 케이스 추가
## Notes
- `PortfolioService.create_portfolio` 시그니처(`owner_user_id: str = "system"`)는 변경하지 않는다. CLI 호출부에서만 실제 UUID를 전달한다.
- REQ-013에서 도입한 `_with_service_and_repo` 헬퍼를 그대로 사용한다. 추가 헬퍼 불필요.
- minor-change fast path 적용: Discovery 생략, Approved 직행. 범위는 CLI 옵션 1개 추가이며 기존 서비스/repository 인터페이스 변경 없음.
- Source Discovery: 없음 (minor-change fast path)
## Change Log
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-014 최초 생성 (Approved 직행). minor-change fast path 적용. REQ-012, REQ-013 선례 동일 경로.
#### Intent
- `pfhub portfolio create`의 소유자 설정 누락 문제를 CLI 레벨에서 해결.
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- BAT-004 verification(44 passed) 기반으로 Approved → Implemented 전환.
#### Intent
- 구현 및 검증 증거 확보 완료 후 상태 동기화.
#### Acceptance Criteria Delta
- Added: AC 1~5 신규 작성
#### Impacted Area Delta
- Docs: README.md create 예제 갱신
- Modules: app/cli/commands/portfolio.py create_portfolio 수정
- Tests: tests/test_cli.py 케이스 추가/수정
#### Delivery Trace Delta
- Batches: none (batch 미생성)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: draft-batch 진행
@@ -1,143 +0,0 @@
# req-015: CLI `credential` 명령 그룹 구현 (브로커 인증 정보 관리)
- Status: Implemented
- Type: Interface
- Priority: Medium
- Owner: repository-maintainers
## Intent
- 운영자가 CLI(`pfhub credential`)를 통해 KIS 브로커 인증 정보를 안전하게 등록, 조회, 삭제할 수 있어야 한다.
- `broker_credentials` 테이블(migration 0014)과 기존 Fernet 암호화 인프라(`app/core/encryption.py`, `CREDENTIAL_ENCRYPTION_KEY`)를 재사용해 CLI 레이어만 추가한다.
## Requirement
- 시스템은 `pfhub credential set --user <username> --broker <broker> --account-ref <ref> --app-key <key> --app-secret <secret>` 명령을 제공해야 한다. 이 명령은 `{"app_key": ..., "app_secret": ...}` JSON을 Fernet으로 암호화한 뒤 `broker_credentials` 테이블에 upsert한다.
- 시스템은 `pfhub credential list --user <username>` 명령을 제공해야 한다. 이 명령은 사용자의 브로커 인증 정보 목록을 반환하되, `encrypted_payload`는 출력하지 않는다.
- 시스템은 `pfhub credential remove --user <username> --broker <broker> [--force]` 명령을 제공해야 한다. 이 명령은 해당 인증 정보 레코드를 삭제한다.
- 존재하지 않는 사용자를 지정하면 에러 메시지와 함께 exit code 1로 종료해야 한다.
## Acceptance Criteria
1. Given alice 사용자와 유효한 `CREDENTIAL_ENCRYPTION_KEY`가 설정된 환경에서, When `pfhub credential set --user alice --broker kis --account-ref REF --app-key KEY --app-secret SECRET`을 실행하면, Then `broker_credentials` 테이블에 암호화된 payload로 레코드가 생성(또는 갱신)된다.
2. Given `broker_credentials`에 alice의 kis 레코드가 있을 때, When `pfhub credential list --user alice`를 실행하면, Then broker, account_ref, credential_version, last_rotated_at이 출력되고 `encrypted_payload`는 포함되지 않는다.
3. Given 존재하지 않는 username을 지정할 때, When `pfhub credential set --user unknown ...`을 실행하면, Then 에러 메시지와 함께 exit code 1로 종료된다.
4. Given alice의 kis 인증 정보가 있을 때, When `pfhub credential remove --user alice --broker kis --force`를 실행하면, Then 해당 레코드가 삭제되고 `pfhub credential list --user alice`에서 더 이상 표시되지 않는다.
5. Given `CREDENTIAL_ENCRYPTION_KEY`가 설정되지 않은 환경에서, When `pfhub credential set ...`을 실행하면, Then 설정 미비 에러 메시지와 함께 exit code 1로 종료된다.
## Impacted Area
- Docs: `docs/project-structure.md` (CLI 모듈 목록 갱신)
- Modules:
- `app/cli/commands/credential.py` (신규 — `credential_app`, `set`, `list`, `remove` 명령)
- `app/cli/main.py` (`credential_app` 등록 추가)
- `app/repositories/postgres.py` (`delete_broker_credential` 메서드 추가)
- Tests: `tests/test_cli.py` (credential 명령 통합 테스트 추가)
## Notes
- `broker_credentials` 테이블 DDL과 `upsert_broker_credential`, `list_broker_credentials`, `get_broker_credential` 메서드는 migration 0014에서 이미 구현됨. `delete_broker_credential` 메서드는 이번 REQ에서 추가.
- `app/core/encryption.py`의 `encrypt_payload(key, plaintext)` 사용.
- 기존 CLI 패턴(`_with_service` 헬퍼, `execute_command`, `render_output`, `_confirm`) 동일하게 적용.
- `--broker` 기본값은 `kis`.
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-005 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-005 verification.md에서 REQ-015 AC-1~5가 모두 passed이고 regression 없음이 확인되어 Implemented로 승격한다.
#### Acceptance Criteria Delta
- Added: none
- Updated: none
- Removed: none
#### Impacted Area Delta
- Docs: none
- Modules: none
- Tests: none
#### Delivery Trace Delta
- Batches:
- BAT-005 (release-candidate)
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: tooling release에 포함 시 credential smoke 절차 포함
#### Validation Plan
- BAT-005 verification.md의 AC-1~5 passed evidence와 residual risk 없음 확인
### CHG-20260503-02
- Date: 2026-05-03
- Author: repository-maintainers
#### Change Summary
- REQ-015 Approved 전환 — minor-change fast path (단일 저위험 REQ, CLI 레이어만 추가, 구조/런타임 영향 없음)
#### Intent
- Acceptance Criteria 5개 모두 구현 판단에 충분하며 blocking assumption 없음
#### Acceptance Criteria Delta
- Added: none
- Updated: none
- Removed: none
#### Impacted Area Delta
- Docs: none
- Modules: none
- Tests: none
#### Delivery Trace Delta
- Batches: BAT-005 (batch-lite)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: run-batch-implementation 진행
#### Validation Plan
- BAT-005 verification 통과 확인
---
### CHG-20260503-01
- Date: 2026-05-03
- Author: repository-maintainers
#### Change Summary
- REQ-015 최초 작성 — CLI `credential` 명령 그룹 구현 (브로커 인증 정보 관리)
#### Intent
- 운영자가 KIS 브로커 인증 정보를 CLI로 관리할 수 있도록 Interface 레이어 추가
#### Acceptance Criteria Delta
- Added:
- 위 Acceptance Criteria 1~5 전체
- Updated: none
- Removed: none
#### Impacted Area Delta
- Docs: `docs/project-structure.md`
- Modules: `app/cli/commands/credential.py` (신규), `app/cli/main.py`, `app/repositories/postgres.py`
- Tests: `tests/test_cli.py`
#### Delivery Trace Delta
- Batches: 미정 (BAT-005 예정)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: 없음
#### Validation Plan
- `tests/test_cli.py`에 credential 명령 통합 테스트 작성 후 `pytest tests/test_cli.py` 전체 통과 확인
@@ -1,186 +0,0 @@
# req-017: CLI portfolio 명령 — name+user 조합 식별 방식 전환
- Status: Implemented
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- 현재 `portfolio_id` Argument로만 portfolio를 식별하는 CLI 뮤테이션 명령을 `<name>` + `--user <username>` 조합으로 전환해 소유권 검증을 명령 실행 경로에 포함한다.
- CLI 내부에서 `(owner_user_id, name)` → UUID 단일 쿼리 조회 후 처리하고, 조회 실패 시 명확한 오류 메시지를 출력한다.
## Requirement
- `portfolio create` 명령은 `name` Argument와 `--user <username>` 옵션을 필수로 받는다. `portfolio_id`는 서버가 UUID로 발급하며 사용자가 직접 지정하거나 조회할 수 없다.
- `status`, `buy`, `sell`, `invest`, `transfer`, `reset`, `delete`, `update` 명령은 `<name>` Argument와 `--user <username>` 옵션으로 portfolio를 식별한다. 내부에서 `(owner_user_id, name)` → `portfolio_id(UUID)`를 단일 DB 쿼리로 조회한 뒤 처리한다.
- `<name>` + `--user` 조합으로 portfolio를 찾지 못하면 오류 메시지 `포트폴리오를 찾을 수 없습니다: {name} (user: {username})`를 출력하고 명령을 중단한다.
- `name`은 `update` 명령으로 변경 가능하다. 변경 시 동일 `owner_user_id` 내에 같은 `name`이 이미 존재하면 오류 메시지 `이미 사용 중인 이름입니다: {name}`를 반환한다.
- `name` 변경 명령에는 명시적 확인 절차(`--force` 또는 confirm 프롬프트)를 요구한다.
- `name` 허용 문자는 `[a-zA-Z0-9._:-]`이며, 공백 불허, 1~100자, 대소문자 구분이 있다. 이 범위를 벗어나면 유효성 검사 오류를 반환한다.
## Acceptance Criteria
1. Given `pfhub portfolio create <name> --user <username> ...`을 실행하면, When 서버가 UUID를 발급하면, Then CLI가 portfolio_id를 직접 지정하거나 조회하지 않고 UUID가 DB에 저장된다.
2. Given `pfhub portfolio buy <name> --user alice ...`를 실행하면, When `(alice, <name>)` 조합으로 DB를 단일 쿼리로 조회해 UUID를 찾으면, Then 해당 portfolio에 buy 작업이 수행된다.
3. Given `--user bob`으로 alice 소유의 portfolio `<name>`을 조작하면, When `(bob, <name>)` 조합으로 조회해 레코드가 없으면, Then `포트폴리오를 찾을 수 없습니다: {name} (user: bob)` 오류가 출력되고 명령이 중단된다.
4. Given alice와 bob이 동일 name `tv`로 각각 portfolio를 생성하면, Then 두 레코드가 독립적으로 저장되고 각자 자신의 `tv` portfolio를 조작할 수 있다.
5. Given alice의 portfolio name을 `tv2`로 변경할 때 alice에게 이미 `tv2` 이름의 portfolio가 존재하면, When `update` 명령이 DB의 `(owner_user_id, name)` UNIQUE 제약 위반을 감지하면, Then `이미 사용 중인 이름입니다: tv2` 오류를 출력한다.
6. Given `--force` 없이 `update` 명령으로 name을 변경하려 하면, Then `--force`가 없으면 변경이 중단되거나 confirm 프롬프트가 표시된다.
7. Given `name` 허용 문자 범위(`[a-zA-Z0-9._:-]`) 밖의 문자가 포함된 이름을 `create` 시 입력하면, Then 유효성 검사 오류가 반환된다.
8. Given `name` 길이가 0이거나 100자를 초과하면, Then 유효성 검사 오류가 반환된다.
## Impacted Area
- Docs: `docs/runtime-flows.md` (CLI 명령 흐름 변경 반영 — REQ-019에서 처리)
- Modules:
- `app/cli/commands/portfolio.py`: `create`, `status`, `buy`, `sell`, `invest`, `transfer`, `reset`, `delete`, `update` 명령의 Argument/Option 전환
- `app/domain/services.py`: `(user_id, name)` → UUID 해석 로직 추가
- `app/repositories/postgres.py`: `get_portfolio_by_name_and_user()` 단일 쿼리 구현 (REQ-016과 공유)
- Tests:
- `tests/test_cli.py`: 소유권 검증 케이스, 오류 메시지 검증, name 유효성 검사, name 중복 변경 거부 테스트
## Notes
- `(owner_user_id, name)` → UUID 조회는 REQ-016에서 추가하는 `get_portfolio_by_name_and_user()` 메서드를 사용한다.
- `name` 대소문자 구분 있음 — `TV`와 `tv`는 다른 이름으로 처리.
- Source Discovery: dcy-004_20260511_portfolio-id-uuid-migration-and-user-scoped-name-resolution (FR-2, FR-3, FR-3a, NFR-2)
## Change Log
### CHG-20260518-01
- Date: 2026-05-18
- Author: repository-maintainers
#### Change Summary
- `get_portfolio_by_name_and_user()` 미구현 결함 수정 — PostgresPortfolioRepository에 메서드가 존재하지 않아 실 DB 환경에서 모든 name+user 식별 경로가 AttributeError로 실패하던 결함을 수정. Status Implemented 유지.
#### Intent
- REQ-017 AC-2~4(name+user 조합으로 portfolio 조회)를 구현한 CLI 코드와 FakeRepo는 완성되어 있었으나, PostgresPortfolioRepository에 `get_portfolio_by_name_and_user()`가 누락되어 실 DB 환경에서 AttributeError가 발생했다. 결함을 수정해 Implemented 근거를 복원한다.
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- Modules:
- `app/repositories/postgres.py`: `get_portfolio_by_name_and_user(owner_user_id, name)` 추가 (`_SELECT_PORTFOLIO WHERE owner_user_id = %s AND name = %s`)
- `app/repositories/portfolio_repository.py`: Protocol에 `get_portfolio_by_name_and_user` 선언 추가
- Tests: `tests/test_portfolio_uuid.py` 14 passed (기존 FakeRepo 기반 테스트는 영향 없음)
#### Delivery Trace Delta
- Batches: BAT-006 (release-candidate) — 결함 수정이 BAT-006 delivery scope에 포함
- Releases: REL-006 (draft) — 배포 전 결함 수정 완료
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: 없음 (실 DB 통합 테스트로 AC-2~4 재확인 권장)
#### Validation Plan
- `pfhub portfolio status <name> --user <user>`, `pfhub portfolio buy <name> --user <user>` 등 name+user 식별 명령이 실 DB 환경에서 정상 응답하는지 확인
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- 최초 생성 — dcy-004 draft-req에 의해 생성
#### Intent
- dcy-004 Discovery의 FR-2(portfolio create 명령 name+user 전환), FR-3(CLI 뮤테이션 명령 식별 방식 전환), FR-3a(name mutable + 중복 변경 거부), NFR-2(단일 DB 쿼리, 오류 메시지) 반영
#### Acceptance Criteria Delta
- Added:
- 1~8 (최초 생성)
#### Impacted Area Delta
- Docs:
- docs/runtime-flows.md: CLI 명령 흐름 변경 (REQ-019에서 처리)
- Modules:
- app/cli/commands/portfolio.py: 명령 Argument/Option 전면 교체
- app/domain/services.py: (user_id, name) → UUID 해석 추가
- app/repositories/postgres.py: get_portfolio_by_name_and_user() 추가
- Tests:
- tests/test_cli.py: 소유권 검증, 오류 메시지, name 유효성 검사 케이스 추가
#### Delivery Trace Delta
- Batches: none (batch 미결정)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req → suggest-batch-reqs → draft-batch
#### Validation Plan
- pytest: 소유권 검증 케이스, 오류 메시지 검증, name 유효성 검사, name 중복 변경 거부, alice/bob 독립 portfolio 생성 검증
### CHG-20260511-02
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Approved 전환 — confirm-req 게이트 통과
#### Intent
- Delivery 입력으로 사용할 수 있도록 REQ-017을 승인 상태로 승격한다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- none
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs로 batch grouping 검토
#### Validation Plan
- confirm-req 게이트 재평가 결과: 필수 메타데이터, 구체적 Acceptance Criteria, Impacted Area, Change Log, blocker 없음 확인
### CHG-20260511-03
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-006 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-006 verification.md의 REQ-017 AC-1~8 모두 test evidence 포함. Blocking Issues 없음. 구현 완료 상태로 전환한다.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-006 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-006)
#### Validation Plan
- BAT-006 verification.md AC-1~8 모두 test evidence 연결 확인 (tests/test_portfolio_uuid.py, tests/test_cli.py 전체 통과)
@@ -1,152 +0,0 @@
# req-018: HTTP API portfolio path parameter UUID 전환 및 v1 asset_id UUID 반환
- Status: Implemented
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- HTTP API의 portfolio 관련 path parameter를 기존 문자열 `portfolio_id`에서 UUID 기반으로 전환해 REQ-016의 스키마 변경과 일관성을 맞춘다.
- `GET /v1/assets` 응답의 `asset_id` 필드를 UUID로 반환해 trex-basic 계약을 갱신하고, dcy-002 A-3을 철회한다.
## Requirement
- `app/api/portfolios.py`의 portfolio 관련 path parameter를 UUID 기반으로 변경한다.
- `GET /v1/assets` 응답의 `asset_id` 필드는 `portfolios.portfolio_id`(UUID)를 반환한다.
- `app/api/v1_assets.py`의 `AssetItem` 직렬화에서 `asset_id`는 `portfolio_id`(UUID) 값을 사용한다.
- `POST /v1/assets/{asset_id}/ledger/trades` 경로의 `asset_id` path parameter는 UUID를 받아 `repo.get(asset_id)`로 portfolio를 조회한다.
- dcy-002 문서의 A-3("asset_id는 portfolio_id를 그대로 사용") 조항은 이번 REQ 구현으로 공식 철회된다. `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md`에 철회 주석을 추가한다.
## Acceptance Criteria
1. Given `GET /v1/assets`를 유효한 service-token으로 호출하면, When 응답 JSON의 `assets[].asset_id` 필드를 확인하면, Then 값이 UUID 형식(`re.match(r'^[0-9a-f-]{36}$', ...)`)이다.
2. Given UUID를 `asset_id`로 사용해 `POST /v1/assets/{asset_id}/ledger/trades`를 호출하면, When 해당 UUID의 portfolio가 존재하면, Then HTTP 201(`status="new"`) 또는 HTTP 200(`status="duplicate"`)으로 정상 처리된다.
3. Given UUID를 `asset_id`로 사용해 `POST /v1/assets/{asset_id}/ledger/trades`를 호출하면, When 해당 UUID의 portfolio가 존재하지 않으면, Then HTTP 404가 반환된다.
4. Given portfolio 관련 HTTP API path(`/portfolios/{portfolio_id}`)를 UUID로 호출하면, When 해당 UUID의 portfolio가 존재하면, Then 정상 응답이 반환된다.
5. Given `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md`를 확인하면, Then A-3 항목에 이번 REQ 구현으로 철회되었다는 주석이 추가되어 있다.
## Impacted Area
- Docs:
- `docs/interface-contract.md` (GET /v1/assets asset_id 변경 반영 — REQ-019에서 처리)
- `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md` (A-3 철회 주석 추가)
- Modules:
- `app/api/portfolios.py`: path parameter UUID 기반 전환
- `app/api/v1_assets.py`: asset_id 직렬화 UUID 사용
- `app/schemas/portfolio.py`, `app/schemas/v1.py`: asset_id/portfolio_id 필드 타입 문서화
- Tests:
- `tests/test_app.py` 또는 `tests/test_v1.py`: GET /v1/assets asset_id UUID 형식 검증, POST /v1/assets/{asset_id}/ledger/trades UUID 호출 정상 동작 확인
## Notes
- trex-basic 측 코드 변경은 이번 REQ 범위 밖. portfolio-hub API 변경만 담당.
- trex-basic과 portfolio-hub는 동시에 배포한다(NFR-3 — 동시 배포 계획 기록은 REQ-019에서 `docs/interface-contract.md`에 처리).
- dcy-002 A-3 철회 주석 추가는 이 REQ 구현 시 함께 수행한다.
- `GET /v1/assets` flow의 `asset_id` 직렬화: `docs/runtime-flows.md` Flow `v1 API — GET /v1/assets` 갱신 필요 (REQ-019에서 처리).
- Source Discovery: dcy-004_20260511_portfolio-id-uuid-migration-and-user-scoped-name-resolution (FR-4)
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- 최초 생성 — dcy-004 draft-req에 의해 생성
#### Intent
- dcy-004 Discovery의 FR-4(HTTP API path parameter UUID 전환, GET /v1/assets asset_id UUID 반환, dcy-002 A-3 철회) 반영
#### Acceptance Criteria Delta
- Added:
- 1~5 (최초 생성)
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md: GET /v1/assets asset_id 변경 반영 (REQ-019에서 처리)
- docs/discovery/dcy-002_*: A-3 철회 주석 추가
- Modules:
- app/api/portfolios.py: path parameter UUID 전환
- app/api/v1_assets.py: asset_id UUID 직렬화
- app/schemas/portfolio.py, app/schemas/v1.py: 필드 타입 변경
- Tests:
- tests/test_app.py 또는 tests/test_v1.py: GET /v1/assets asset_id UUID 형식 검증
#### Delivery Trace Delta
- Batches: none (batch 미결정)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req → suggest-batch-reqs → draft-batch
#### Validation Plan
- pytest: GET /v1/assets asset_id UUID 형식 검증, POST /v1/assets/{asset_id}/ledger/trades UUID 호출 동작 검증, HTTP 404 반환 확인
### CHG-20260511-02
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Approved 전환 — confirm-req 게이트 통과
#### Intent
- Delivery 입력으로 사용할 수 있도록 REQ-018을 승인 상태로 승격한다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- none
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs로 batch grouping 검토
#### Validation Plan
- confirm-req 게이트 재평가 결과: 필수 메타데이터, 구체적 Acceptance Criteria, Impacted Area, Change Log, blocker 없음 확인
### CHG-20260511-03
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-006 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-006 verification.md의 REQ-018 AC-1~5 모두 evidence 포함 (AC-1~4: 구현 코드 검증, AC-5: dcy-002 A-3 철회 주석 문서 확인). Blocking Issues 없음.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-006 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-006); trex-basic 동시 배포 조정 필요
#### Validation Plan
- BAT-006 verification.md AC-1~5 evidence 연결 확인; dcy-002 A-3 철회 주석 직접 확인 완료
@@ -1,143 +0,0 @@
# REQ-023: Daemon Registry 관리 경로
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- service-token issue/rotate가 기존 daemon에만 binding할 수 있으려면, daemon을 사전에 생성·수정할 수 있는 별도 관리 경로(daemon registry CRUD)가 필요하다.
- daemon provisioning은 service-token issuance와 분리된 경로에서 수행되어야 하며, REQ-005(issuance binding)와 REQ-012(rotate daemon_id 유지)의 전제 조건이 된다.
## Requirement
- 시스템은 daemon을 생성·수정할 수 있는 별도 관리 경로를 제공해야 한다.
- daemon은 owner user 내에서 unique한 `daemon_slug`(lowercase kebab-case)와 `daemon_display_name`(non-empty human-readable label)을 가진다.
- 동일 owner user 내에서 중복 `daemon_slug`로 생성 시 명시적으로 거부한다.
- daemon list 조회는 owner user 범위로 제한된다.
## Acceptance Criteria
1. Given admin context에서 유효한 `daemon_slug`와 `daemon_display_name`으로 daemon create를 호출할 때, Then 새 daemon entry가 registry에 생성되고 `daemon_id`가 반환된다.
2. Given 기존 daemon에 대해 `daemon_display_name` 수정을 요청할 때, Then daemon entry가 갱신된다.
3. Given 동일 owner user 내에 이미 존재하는 `daemon_slug`로 daemon create를 요청할 때, Then 명시적 오류로 거부되고 중복 entry가 생성되지 않는다.
4. Given daemon list 조회를 요청할 때, Then 인증된 user 소유의 daemon만 반환된다.
5. Given `pfhub portfolio update <name> --user <username> --daemon-id <daemon_id>`를 실행할 때, Then 해당 portfolio의 `managed_by_daemon_id`가 지정한 daemon_id로 갱신된다.
6. Given `pfhub portfolio list --user <username>`을 실행할 때, Then `managed_by_daemon_id`가 설정된 portfolio에는 해당 daemon_id가 출력에 포함되고, 미설정 portfolio에는 해당 항목이 출력되지 않는다.
## Impacted Area
- Docs: `docs/interface-contract.md`, `docs/runtime-flows.md`
- Modules: `app/domain/models.py`, `app/repositories/postgres.py`, `app/api/auth_routes.py` 또는 별도 daemon management route, `app/cli/commands/` (daemon subcommand 신규)
- Modules (AC-5~6 추가): `app/cli/commands/portfolio.py` (`update` 명령 `--daemon-id` 옵션), `app/schemas/portfolio.py` (`UpdatePortfolioRequest.managed_by_daemon_id`, `PortfolioSummary.managed_by_daemon_id`), `app/services/portfolio_service.py` (`_to_portfolio_summary` 필드 전달), `app/cli/core/utils.py` (`print_portfolio_list` daemon 표시)
- Tests: 신규 `tests/test_daemon_registry.py`
## Notes
- service-token issuance의 unknown daemon 거부(REQ-005 AC-6), rotate daemon_id 유지 불변식(REQ-012 CHG-20260513-01)은 이 REQ의 daemon registry가 사전 구축된 상태를 전제한다.
- daemon registry CRUD의 구체적인 transport 형태(HTTP endpoint vs CLI subcommand)는 구현 시 결정한다.
- canonical 운영 표기: `<username>/<daemon_slug>`
## Change Log
### CHG-20260518-01
- Date: 2026-05-18
- Author: repository-maintainers
#### Change Summary
- AC-5(`portfolio update --daemon-id`), AC-6(`portfolio list` daemon-id 표시) 추가 및 Impacted Area 보강. 구현 완료. Status Approved 유지.
#### Intent
- daemon provisioning runbook에서 "asset → daemon 배정" 단계가 CLI로 수행 가능해야 한다. 기존 AC-1~4는 daemon registry CRUD만 다루며 portfolio-daemon 배정 경로가 누락되어 있었다. 운영팀 요청(2026-05-18)에 따라 `portfolio update --daemon-id` CLI 옵션과 `portfolio list` 결과의 daemon-id 표시를 REQ 범위에 추가한다.
#### Acceptance Criteria Delta
- Added:
- AC-5: `pfhub portfolio update <name> --user <username> --daemon-id <daemon_id>`로 `managed_by_daemon_id` 갱신
- AC-6: `pfhub portfolio list --user <username>` 결과에 `managed_by_daemon_id` 조건부 표시
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Modules:
- `app/cli/commands/portfolio.py`: `update` 명령에 `--daemon-id` 옵션 추가
- `app/schemas/portfolio.py`: `UpdatePortfolioRequest.managed_by_daemon_id`, `PortfolioSummary.managed_by_daemon_id` 필드 추가
- `app/services/portfolio_service.py`: `_to_portfolio_summary`에 `managed_by_daemon_id` 전달 추가
- `app/repositories/postgres.py`: `update_portfolio` allowed dict에 `managed_by_daemon_id` 추가
- `app/cli/core/utils.py`: `print_portfolio_list`에 daemon 줄 조건부 출력 추가
#### Delivery Trace Delta
- Batches: BAT-010 (release-candidate) — AC-5~6 구현이 BAT-010 delivery scope에 포함
- Releases: REL-006 (draft)
#### Revalidation Impact
- Reverification Needed: yes (AC-5~6 신규이므로 검증 증거 추가 필요)
- Existing Implementation Invalidated: no (AC-1~4 기존 구현 유효)
- Status Recommendation: keep-current (Approved)
- Follow-up Action: BAT-010 verification.md에 AC-5~6 검증 증거 추가 후 Implemented 전환 가능
#### Validation Plan
- `pfhub portfolio update <name> --user <user> --daemon-id <id> --force` 실행 후 DB `portfolios.managed_by_daemon_id` 값 확인
- `pfhub portfolio list --user <user>` 출력에서 daemon-id 유무 항목별 표시 확인
### CHG-20260513-02
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- Status: Proposed → Approved (confirm-req 게이트 통과)
#### Intent
- 필수 필드, AC, blocker 없음 확인 후 승인 전환
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- Modules: 없음
- Tests: 없음
#### Delivery Trace Delta
- Batches: 미정
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: 없음
#### Validation Plan
- 없음 (상태 전환만)
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- dcy-006 FR-1의 daemon provisioning 관리 경로 부분만 추출해 신규 생성 (issuance binding·rotate invariant·fail-closed는 REQ-005/012/006/008에 이미 반영)
#### Intent
- service-token issuance(REQ-005)와 rotate(REQ-012)의 전제가 되는 daemon registry CRUD 경로를 별도 REQ로 정의
#### Acceptance Criteria Delta
- Added:
- AC-1~4 전체 신규 추가
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: `docs/interface-contract.md`, `docs/runtime-flows.md`
- Modules: daemon registry route, CLI subcommand
- Tests: `tests/test_daemon_registry.py`
#### Delivery Trace Delta
- Batches: 미정
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: 없음 (신규 REQ)
#### Validation Plan
- pytest로 daemon create/duplicate reject/update/list 시나리오 통과 확인
@@ -1,69 +0,0 @@
# REQ-029: CLI `portfolio list` 출력 — 테두리 없는 리스트 형식
- Status: Approved
- Type: Interface
- Priority: Low
- Owner: repository-maintainers
## Intent
- `pfhub portfolio list` 기본 출력을 Rich Table(테두리 포함) 대신 항목별 텍스트 블록으로 변경해 터미널 폭에 구애받지 않는 가독성을 확보한다.
- JSON 출력(`--format json`)은 기존과 동일하게 유지한다.
## Requirement
- `pfhub portfolio list --user <username>` 명령의 기본 출력(text 형식)은 테두리·구분선 없는 리스트 형태로 각 portfolio를 표시해야 한다.
- 각 항목은 3줄 블록으로 구성한다: (1) name/symbol/status/broker [tags], (2) cash/qty/price/buffer/total 수치, (3) portfolio_id.
- `managed_by_daemon_id`가 설정된 항목은 (3) 다음 줄에 daemon_id를 추가로 표시하고, 미설정 항목은 해당 줄을 생략한다.
- 항목 간에 빈 줄 한 줄로 구분한다.
- `--format json` 옵션 사용 시 기존 JSON 응답을 그대로 반환한다.
## Acceptance Criteria
1. Given `pfhub portfolio list --user <username>` 실행 시, Then Rich Table 형식(헤더·테두리)이 아닌 텍스트 블록 리스트가 출력된다.
2. 각 portfolio 항목은 name, symbol, status, broker 정보와 cash/qty/price/buffer/total 수치, portfolio_id를 포함한다.
3. Given `managed_by_daemon_id`가 설정된 portfolio가 있을 때, Then 해당 항목에 daemon_id 줄이 추가로 표시된다.
4. Given `pfhub portfolio list --user <username> --format json` 실행 시, Then JSON 배열이 반환되며 텍스트 블록 출력이 표시되지 않는다.
## Impacted Area
- Docs: 없음
- Modules:
- `app/cli/core/utils.py`: `print_portfolio_table` 제거, `print_portfolio_list` 신규 구현, `render_output`에서 `print_portfolio_list` 호출
- Tests: 기존 `tests/test_portfolio_uuid.py` 출력 내용 미검증이므로 별도 영향 없음
## Notes
- 이 변경은 CLI 출력 형식만 변경하며 API, 데이터 모델, DB 스키마에는 영향을 주지 않는다.
- REQ-023 AC-6(managed_by_daemon_id 표시)와 구현을 공유한다.
## Change Log
### CHG-20260518-01
- Date: 2026-05-18
- Author: repository-maintainers
#### Change Summary
- 최초 생성 및 Approved 전환 — 운영팀 요청(2026-05-18)에 따라 `portfolio list` 출력 형식을 테두리 없는 리스트 형태로 변경 완료. 구현 및 사용자 확인 완료.
#### Intent
- Rich Table 형식은 터미널 폭이 좁거나 항목 수가 많을 때 가독성이 떨어진다. 텍스트 블록 리스트는 항목별 스캔이 쉽고 터미널 폭 의존성이 없다.
#### Acceptance Criteria Delta
- Added: AC-1~4 (최초 생성)
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Modules:
- `app/cli/core/utils.py`: `print_portfolio_table` → `print_portfolio_list` 교체, REQ-023 AC-6 daemon 줄 출력 포함
#### Delivery Trace Delta
- Batches: 미정 (minor-change fast path로 batch-lite 생성 예정)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no (신규 REQ, 기존 구현 무효화 없음)
- Existing Implementation Invalidated: no
- Status Recommendation: Approved
- Follow-up Action: batch-lite 생성 → 검증 → confirm-req-implemented
#### Validation Plan
- `pfhub portfolio list --user <username>` 실행 결과에 Rich Table 헤더·테두리가 없는지 확인
- daemon-id 있는 항목과 없는 항목 혼재 시 조건부 표시 확인
- `--format json` 옵션 시 JSON 반환 확인
@@ -1,147 +0,0 @@
# req-002: Alembic와 runtime schema ownership 경계 정리
- Status: Implemented
- Type: Migration
- Priority: High
- Owner: repository-maintainers
## Intent
- 이 요구사항은 Alembic migration 경로와 PostgresPortfolioRepository.ensure_schema() 경로가 같은 schema 객체를 다룰 때 발생할 수 있는 drift와 재실행 실패를 줄이기 위한 경계 규칙을 정의한다.
- 로컬 docker compose 환경에서 fresh DB와 runtime-precreated schema 상태를 기준으로 허용 순서, 사전 점검, 보정 규칙을 문서화하는 것이 목표다.
## Requirement
- 프로젝트는 로컬 docker compose 기반 DB 환경에서 Alembic migration과 runtime schema bootstrap이 다루는 테이블과 인덱스의 ownership 경계를 식별해야 하며, migrate-db 실행 시 허용되는 순서, 충돌 가능 지점, 사전 점검 규칙, 보정 원칙을 명시해야 한다.
## Acceptance Criteria
1. Given app/repositories/postgres.py와 migrations/versions/ 아래 현재 revision들이 있을 때, When schema ownership 분석을 수행하면, Then Alembic과 runtime ensure_schema()가 다루는 주요 객체와 충돌 가능 지점이 식별되어 있어야 한다.
2. Given 로컬 docker compose 기반 fresh DB와 app runtime이 일부 schema를 먼저 만든 DB 시나리오가 있을 때, When migrate-db 실행 규칙을 정의하면, Then 허용 순서, 금지 순서, 필요한 사전 점검과 보정 규칙이 문서로 정리되어 있어야 한다.
3. Given 코드 안에 legacy schema stamp 분기가 남아 있을 때, When migration 경계 문서를 작성하면, Then 이번 REQ와 관련 batch 범위에서 legacy DB 지원이 제외된다는 점이 명시되어 있어야 한다.
## Impacted Area
- Docs: docs/project-structure.md, docs/runtime-flows.md, docs/data-model.md
- Modules: app/repositories/postgres.py, app/db/migrations.py, migrations/versions/*.py
- Tests: 로컬 schema ownership 검토 체크리스트 또는 migrate-db 충돌 시나리오 검증 절차
## Notes
- 이 요구사항은 dcy-001의 FR-2, FR-3 일부, S-2, R-1, R-2를 구현 가능한 Migration 단위로 정규화한 것이다.
- legacy DB support와 legacy stamp 경로 검증은 Discovery 범위에서 이미 제외되었으므로, 이 REQ는 local runtime bootstrap 흔적과 Alembic의 상호작용에 집중한다.
- 문서화만으로 해결되지 않는 ownership 충돌이 발견되면 후속 batch에서 코드 수정이 필요할 수 있다.
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-007 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-007 verification.md AC-1~3 모두 PASS. Blocking Issues 없음. docs/project-structure.md "Schema Ownership Boundary", docs/runtime-flows.md "Alembic vs ensure_schema() Schema Ownership Boundary" evidence 연결 확인 완료. 잔여 리스크(idx_auth_tokens_service_name_unique Alembic 미등록)는 후속 batch 검토 항목으로 남겨둠.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-007 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-007); idx_auth_tokens_service_name_unique Alembic revision 추가 검토 권장
#### Validation Plan
- BAT-007 verification.md AC-1~3 evidence 연결 확인 완료
### CHG-20260430-02
- Date: 2026-04-30
- Author: GitHub Copilot
#### Change Summary
- confirm-req 승인 게이트를 통과해 이 REQ를 Approved로 전환했다.
#### Intent
- dcy-001의 delivery input으로 사용할 수 있는 확정 requirement로 승격하기 위해 상태를 변경했다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- docs/srs/index.md
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: approved
- Follow-up Action: suggest-batch-reqs 또는 draft-batch 단계에서 REQ-002의 ownership 문서를 delivery scope로 연결한다.
#### Validation Plan
- REQ 본문 필수 섹션, Acceptance Criteria 구체성, docs/srs/index.md 상태 동기화를 확인한다.
### CHG-20260430-01
- Date: 2026-04-30
- Author: GitHub Copilot
#### Change Summary
- dcy-001에서 Alembic와 runtime schema ownership 경계 요구사항을 신규 Proposed REQ로 생성했다.
#### Intent
- migrate-db와 runtime bootstrap의 경계가 불분명해 생길 수 있는 drift와 재실행 실패를 독립 Migration backlog로 관리하기 위해 생성했다.
#### Acceptance Criteria Delta
- Added:
- Alembic과 runtime ensure_schema() ownership 분석 기준 정의
- local runtime-precreated schema 시나리오용 허용 순서와 보정 규칙 정의
- legacy DB 비지원 범위 명시
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- docs/project-structure.md
- docs/runtime-flows.md
- docs/data-model.md
- Modules:
- app/repositories/postgres.py
- app/db/migrations.py
- migrations/versions/*.py
- Tests:
- migrate-db 충돌 시나리오 검토 체크리스트 또는 동등한 검증 절차
#### 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 단계에서 ownership 경계의 대상 객체와 범위 제외 항목을 검토하고 승인한다.
#### Validation Plan
- confirm-req 전까지 Acceptance Criteria가 dcy-001의 FR-2, FR-3 일부, S-2와 일치하는지 검토하고, 이후 batch 단계에서 실제 객체 목록과 허용 순서 문서를 작성한다.
@@ -0,0 +1,79 @@
# REQ-006: portfolio_tags 데이터 마이그레이션
- Status: Proposed
- Type: Migration
- Priority: High
- Owner: Justin Ko
## Intent
- 기존 portfolio_tags.tag_value 자유 문자열 데이터를 tags 마스터 테이블로 이전하고, portfolios.tag_ids UUID[] 배열을 채운 후 portfolio_tags 테이블을 제거한다.
- 소문자 정규화 후 UNIQUE 충돌을 방지하고, 마이그레이션 전 지정 admin 계정의 존재 여부를 사전 검증하여 데이터 무결성을 확보한다.
- 마이그레이션된 기존 태그는 사용자 포트폴리오에서 유래했으므로 is_admin_tag=FALSE로 설정한다.
## Requirement
- 마이그레이션 스크립트는 실행 전 운영자가 지정한 admin 계정 ID가 users 테이블에 존재하는지 검증해야 한다. 존재하지 않으면 오류와 함께 중단한다.
- portfolio_tags.tag_value를 소문자 정규화(str.lower()) 후 tags 테이블에 INSERT해야 한다. ON CONFLICT DO NOTHING으로 중복을 무시한다.
- 모든 INSERT는 is_admin_tag=FALSE, created_by_user_id=운영자 지정 admin 계정 ID로 설정해야 한다.
- 각 portfolio의 portfolio_tags 행을 순회하여 해당 tag_value의 tag_id를 조회하고 portfolios.tag_ids 배열에 추가해야 한다.
- 데이터 이전 완료 후 portfolio_tags 테이블을 DROP해야 한다.
- 마이그레이션은 Alembic revision으로 관리하여 재실행 안전성(idempotent within revision)을 보장해야 한다.
## Acceptance Criteria
1. Given portfolio_tags 행이 존재하고 유효한 admin 계정 ID 지정, When 마이그레이션 실행, Then tags 테이블에 소문자 정규화된 DISTINCT tag_value 수만큼 행이 생성되고 모두 is_admin_tag=FALSE, created_by_user_id=지정 admin ID이다.
2. Given portfolio_tags에 "TestTag"와 "testtag"가 존재, When 마이그레이션 실행, Then tags 테이블에 "testtag" 행이 하나만 생성된다 (ON CONFLICT DO NOTHING).
3. Given 마이그레이션 실행 후, When 각 portfolio의 tag_ids를 조회, Then 기존 portfolio_tags에서 해당 portfolio에 연결된 tag_value들의 tag_id가 모두 포함된다.
4. Given 마이그레이션 실행 후, When DB 스키마를 조회, Then portfolio_tags 테이블이 존재하지 않는다.
5. Given 유효하지 않은 admin 계정 ID(users 테이블에 없음) 지정, When 마이그레이션 실행, Then 데이터 변경 없이 오류와 함께 중단된다.
6. Given 마이그레이션 전 DISTINCT tag_value 수(소문자 정규화 기준) = N, When 마이그레이션 실행 후 tags 테이블 행 수 조회, Then N이다.
## Impacted Area
- Docs: docs/data-model.md (portfolio_tags 테이블 제거 반영)
- Modules:
- migrations/versions/ — 데이터 마이그레이션 Alembic revision (태그 이전, portfolios.tag_ids 채우기, portfolio_tags DROP)
- Tests:
- 마이그레이션 전후 DB 상태 SQL 검증
- 잘못된 admin 계정 ID 시 중단 확인
- DISTINCT 수 일치 확인
## Notes
- 운영자는 마이그레이션 실행 전 admin 계정 ID를 확인하여 마이그레이션 스크립트에 명시한다.
- 마이그레이션된 태그의 is_admin_tag=FALSE는 의도적 설계: 기존 portfolio_tags는 사용자 포트폴리오에서 유래했으며 asset 태그 적용 대상이 아니다.
- REQ-003의 portfolios.tag_ids 컬럼 추가가 이 REQ 선행 조건이다.
- 마이그레이션 완료 후 별도 소유권 재지정 스크립트 제공은 R-1 완화 방안으로, 이 REQ 범위 밖이다.
- Source: dcy-002 FR-5, R-1, R-3, A-2
## Change Log
### CHG-20260519-01
- Date: 2026-05-19
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
#### Intent
- dcy-002 FR-5를 Migration REQ로 분리하여 독립 구현·검증 단위로 정규화
#### Acceptance Criteria Delta
- Added:
- AC-1~6 최초 작성
#### Impacted Area Delta
- Docs:
- docs/data-model.md — portfolio_tags 제거
- Modules:
- migrations/versions/ — 최초 지정
- 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-001, REQ-003 선행 의존성 확인
@@ -1,184 +0,0 @@
# req-009: v1 API 지원을 위한 DB 스키마 migration 추가
- Status: Approved
- Type: Migration
- Priority: High
- Owner: repository-maintainers
## Intent
- dcy-002에서 요구하는 4개 DB 변경(portfolios.market 컬럼, auth_tokens.runtime_id 컬럼, broker_credentials 테이블, ledger_trades 테이블)을 Alembic revision으로 구현한다.
- dcy-006의 daemon-aware authorization 모델을 위해 `daemons` registry, `auth_tokens.daemon_id`, `portfolios.managed_by_daemon_id`, transition backfill 및 관련 제약을 같은 migration ownership 규칙 안에서 확장한다.
- IF NOT EXISTS 패턴을 유지해 REQ-002 정책을 준수한다(NFR-4).
## Requirement
- 다음 스키마 변경을 IF NOT EXISTS 패턴을 사용하는 신규 Alembic migration으로 추가해야 한다.
1. portfolios 테이블에 market TEXT NULLABLE 컬럼 추가 (기존 행은 NULL로 유지)
2. auth_tokens 테이블에 runtime_id TEXT UNIQUE NULLABLE 컬럼 추가
3. broker_credentials 테이블 신규 생성 (user_id, broker, account_ref, encrypted_payload, credential_version, last_rotated_at 포함)
4. ledger_trades 테이블 신규 생성 (ledger_entry_id PK, asset_id, order_id, execution_id, side, quantity, price, fee_amount, tax_amount, executed_at, recorded_at 포함, (asset_id, order_id, execution_id) unique constraint 포함)
5. daemons registry 테이블 신규 생성 (`daemon_id`, `owner_user_id`, stable slug/display metadata, 생성/갱신 시각 포함)
6. auth_tokens 테이블에 `daemon_id` NULLABLE FK 컬럼 추가, portfolios 테이블에 `managed_by_daemon_id` NULLABLE FK 컬럼 추가
7. transition migration은 기존 service-token과 portfolio의 daemon 관련 컬럼을 `NULL`로 backfill하고, post-cutover exact-one assignment로 수렴할 수 있도록 same-user consistency 및 invalid reference rejection을 위한 FK/check/unique 제약을 제공해야 한다.
## Acceptance Criteria
1. Given alembic upgrade head 실행 후, When portfolios 테이블을 조회하면, Then market TEXT NULLABLE 컬럼이 존재해야 한다.
2. Given alembic upgrade head 실행 후, When auth_tokens 테이블을 조회하면, Then runtime_id TEXT NULLABLE UNIQUE 컬럼이 존재해야 한다.
3. Given alembic upgrade head 실행 후, Then broker_credentials 테이블이 존재해야 한다.
4. Given alembic upgrade head 실행 후, Then ledger_trades 테이블이 존재해야 한다.
5. Given (asset_id, order_id, execution_id) 동일 조합으로 ledger_trades에 두 번 삽입 시, Then UniqueViolation이 발생해야 한다.
6. Given migration을 이미 실행한 DB에 동일 migration을 재실행하면, Then 에러 없이 완료되어야 한다(IF NOT EXISTS 보장).
7. Given portfolios 테이블의 기존 행, When migration 실행 후 조회하면, Then market 값이 NULL이어야 한다(기존 데이터 보정 없음, DECIDE_MARKET_DEFAULT Answered).
8. Given alembic upgrade head 실행 후, When schema를 조회하면, Then `daemons` 테이블과 `auth_tokens.daemon_id` 컬럼이 존재해야 한다.
9. Given alembic upgrade head 실행 후, When portfolios 테이블을 조회하면, Then `managed_by_daemon_id` 컬럼이 존재하고 기존 행은 `NULL`로 backfill되어 있어야 한다.
10. Given 존재하지 않는 `daemon_id`를 auth_tokens 또는 portfolios에 바인딩하려고 시도하면, Then FK 또는 동등 제약으로 거부되어야 하며, owner user 일관성을 깨는 binding/assignment는 DB 레벨 제약 또는 동등한 무결성 장치로 방지되어야 한다.
## Impacted Area
- Docs: docs/data-model.md (REQ-011에서 갱신 예정)
- Modules: migrations/versions/, app/domain/models.py, app/repositories/portfolio_repository.py, app/repositories/postgres.py
- Tests: tests/test_app.py 또는 migration 검증 테스트, daemon schema integrity 검증
## Notes
- NFR-4(IF NOT EXISTS 패턴) 준수 — REQ-002 Alembic migration 정책 기반.
- broker_credentials 테이블의 encrypted_payload 컬럼은 Fernet 암호화된 ciphertext를 TEXT로 저장한다. 암복호화 로직은 REQ-010 참조.
- 컬럼 상세 타입(NUMERIC vs FLOAT, TIMESTAMPTZ 등)은 설계 단계(draft-batch-design)에서 확정한다.
- daemon exact-one assignment의 steady-state `NOT NULL`/강제 cutover는 inventory/backfill 완료 이후 단계적으로 적용할 수 있다. transition migration은 `NULL` backfill을 허용하되 fail-open을 허용하면 안 된다.
- migration revision ID는 구현 시 기존 revision chain의 마지막 head를 down_revision으로 참조한다.
- Source Discovery: dcy-002 (영향 범위, NFR-4, DECIDE_MARKET_DEFAULT), dcy-006 (FR-1, FR-2, NFR-2, NFR-3)
## Change Log
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
#### Change Summary
- runtime_id 중심 v1 migration REQ를 daemon registry/binding/assignment 스키마까지 포함하도록 확장하고, 기존 Implemented 상태를 Approved로 되돌림
#### Intent
- dcy-006의 daemon-aware model은 기존 REQ-009 구현 범위를 넘어서 `daemons`, `auth_tokens.daemon_id`, `portfolios.managed_by_daemon_id`, transition backfill, same-user consistency 제약을 추가로 요구한다. BAT-001/REL-001 migration evidence는 이 범위를 포함하지 않는다.
#### Acceptance Criteria Delta
- Added:
- AC-8: `daemons`, `auth_tokens.daemon_id`
- AC-9: `portfolios.managed_by_daemon_id` + NULL backfill
- AC-10: daemon FK / 무결성 제약
- Updated:
- Requirement 본문에 daemon registry 및 transition backfill 항목 추가
- Removed:
- none
#### Impacted Area Delta
- Docs:
- `docs/data-model.md`, `docs/runtime-flows.md`
- Modules:
- `migrations/versions/`, `app/domain/models.py`, `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`
- Tests:
- migration integrity / daemon schema constraint 검증
#### Delivery Trace Delta
- Batches:
- BAT-001 (release-candidate; REL-001 released input) — 기존 migration verification은 daemon schema, backfill, constraint evidence를 포함하지 않음
- Releases:
- REL-001 (released) — release facts는 runtime_id/broker_credentials/ledger_trades까지만 다루므로 revised migration REQ를 충족하지 않음
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: yes
- Status Recommendation: rollback-to-approved
- Follow-up Action: dcy-006 후속 batch에서 신규 Alembic revision과 migration validation을 설계하고, cutover/backfill evidence를 release 단계에서 수집한다.
#### Validation Plan
- 현재 migration chain과 `app/repositories/postgres.py` schema ownership을 재검토해 daemon schema 추가 위치를 설계한다.
- 구현 후 alembic upgrade / re-run / invalid daemon reference 검증을 포함한 migration test plan을 수행한다.
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-001 re-verified verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-001 verification.md (re-verified, 2026-05-03) AC-1~7 모두 코드 검토 PASS. DB 환경 실행 미확인은 non-blocker로 처리됨 (revision에 IF NOT EXISTS 패턴 전체 적용 확인). Blocking Issues 없음.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-001 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-001); 배포 전 Docker Compose 환경에서 실제 migration 실행 검증 권장
#### Validation Plan
- BAT-001 verification.md (re-verified) AC-1~7 코드 검토 evidence 연결 확인 완료
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-009 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 7개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### 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: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-009 최초 생성 (source Discovery: dcy-002)
#### Intent
- v1 API 지원에 필요한 DB 스키마 변경을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/data-model.md (REQ-011에서 갱신 예정)
- Modules:
- migrations/versions/ 신규 migration 파일
- app/domain/models.py 모델 필드 추가
- Tests:
- migration 검증 테스트
#### 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-005, REQ-006, REQ-007, REQ-008과 함께 delivery 단위 구성 필수(runtime_id, market, broker_credentials, ledger_trades 모두 이 migration에 의존).
@@ -1,157 +0,0 @@
# req-016: portfolios 테이블 UUID 전환 및 데이터 마이그레이션
- Status: Implemented
- Type: Migration
- Priority: High
- Owner: repository-maintainers
## Intent
- 현재 사용자가 직접 지정하는 TEXT PRIMARY KEY(`portfolio_id`)를 서버 생성 UUID로 교체하고, 기존 `portfolio_id` 문자열을 `name` 컬럼으로 이전한다.
- `(owner_user_id, name)` 복합 UNIQUE 제약을 DB 레벨에서 도입해 사용자 범위 내 이름 유일성을 보장한다.
- 기존 데이터와 FK 참조 테이블(`portfolio_ledger`, `portfolio_comments`, `portfolio_tags`, `ledger_trades`)을 단일 Alembic revision으로 atomic하게 마이그레이션한다.
## Requirement
- 단일 Alembic revision을 추가해 아래를 수행한다.
1. `portfolios` 테이블에 `name` TEXT 컬럼을 추가한다.
2. 기존 `portfolio_id` 값을 `name` 컬럼으로 복사한다 (변환 없이 그대로).
3. 각 기존 레코드에 `uuid.uuid4()`로 새 UUID를 생성해 `portfolio_id` 컬럼 값을 교체한다.
4. FK 참조 테이블(`portfolio_ledger`, `portfolio_comments`, `portfolio_tags`, `ledger_trades`)의 `portfolio_id` 컬럼 값도 새 UUID로 함께 갱신한다.
5. `(owner_user_id, name)` 복합 UNIQUE 제약을 `portfolios` 테이블에 추가한다.
6. `Portfolio` dataclass(`app/domain/models.py`)의 `portfolio_id` 필드를 UUID 문자열로 유지하고 `name` 필드를 명확히 선언한다.
7. `app/repositories/postgres.py`의 `create_portfolio()` 메서드가 서버에서 `uuid.uuid4()`로 생성된 UUID를 `portfolio_id`로 사용한다.
- 마이그레이션은 IF NOT EXISTS 패턴을 준수해 재실행 시 오류가 없어야 한다(REQ-002 준수).
- FK 참조 테이블과의 갱신은 단일 트랜잭션 내에서 atomic하게 처리한다.
- 마이그레이션 완료 후 FK 고아 레코드(orphan) 0건을 검증하는 쿼리를 revision 안에 포함한다.
## Acceptance Criteria
1. Given 기존 `portfolio_id` 문자열로 운영 중인 portfolios 레코드가 있을 때, When 신규 Alembic revision을 `alembic upgrade head`로 실행하면, Then 각 레코드의 `name` 컬럼에 기존 `portfolio_id` 값이 그대로 복사되고, `portfolio_id`는 UUID 형식(`re.match(r'^[0-9a-f-]{36}$', ...)`)으로 교체된다.
2. Given 마이그레이션 완료 후, When `portfolio_ledger`, `portfolio_comments`, `portfolio_tags`, `ledger_trades`의 `portfolio_id` 컬럼을 확인하면, Then 고아 레코드(FK 불일치 레코드)가 0건이다.
3. Given `(owner_user_id, name)` 복합 UNIQUE 제약이 적용된 상태에서, When 동일 `owner_user_id`와 동일 `name`으로 portfolio 레코드를 추가하면, Then DB 레벨에서 UNIQUE 제약 위반 오류가 발생한다.
4. Given 동일 `name`이지만 다른 `owner_user_id`로 portfolio 레코드를 추가하면, Then 정상 삽입된다.
5. Given 신규 Alembic revision을 이미 적용한 환경에서, When 동일 revision을 재실행하면, Then 오류 없이 완료된다(IF NOT EXISTS 준수).
6. Given `app/repositories/postgres.py`의 `create_portfolio()` 메서드가 실행되면, Then 서버에서 `uuid.uuid4()`로 생성된 UUID가 `portfolio_id`로 저장된다.
## Impacted Area
- Docs: `docs/data-model.md` (portfolios 테이블 스키마 변경 반영 — REQ-019에서 처리)
- Modules:
- `migrations/versions/`: 신규 Alembic revision 파일
- `app/domain/models.py`: Portfolio dataclass `portfolio_id` 및 `name` 필드
- `app/repositories/postgres.py`: `create_portfolio()` UUID 생성, `get_portfolio_by_name_and_user()` 신규 메서드
- Tests:
- 신규 `tests/test_portfolio_uuid.py` 또는 `tests/test_cli.py` — portfolio_id UUID 형식 검증, 동일 name 독립 생성 테스트, (owner_user_id, name) UNIQUE 제약 동작 확인
## Notes
- 기존 `portfolio_id` 문자열은 `name` 컬럼으로 변환 없이 복사한다(Discovery A-3 확정).
- `portfolio_id` 컬럼 타입은 TEXT 유지(PostgreSQL UUID 타입으로의 전환은 이번 범위 밖).
- `app/domain/models.py`의 `Portfolio.portfolio_id`는 UUID 문자열로 의미가 바뀌나 Python 타입(`str`)은 유지.
- `get_portfolio_by_name_and_user()` 메서드는 REQ-017(CLI 식별 방식 전환)에서도 사용한다.
- Source Discovery: dcy-004_20260511_portfolio-id-uuid-migration-and-user-scoped-name-resolution (FR-1, FR-5, NFR-1)
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- 최초 생성 — dcy-004 draft-req에 의해 생성
#### Intent
- dcy-004 Discovery의 FR-1(portfolios.portfolio_id UUID 교체 및 name 컬럼 추가), FR-5(기존 데이터 마이그레이션), NFR-1(단일 revision, IF NOT EXISTS, atomic 트랜잭션) 반영
#### Acceptance Criteria Delta
- Added:
- 1~6 (최초 생성)
#### Impacted Area Delta
- Docs:
- docs/data-model.md: portfolios 테이블 스키마 변경 (REQ-019에서 처리)
- Modules:
- migrations/versions/: 신규 revision 추가
- app/domain/models.py: Portfolio dataclass 변경
- app/repositories/postgres.py: create_portfolio UUID 생성, get_portfolio_by_name_and_user() 추가
- Tests:
- tests/test_portfolio_uuid.py 또는 tests/test_cli.py: UUID 형식 검증, UNIQUE 제약 동작 확인
#### Delivery Trace Delta
- Batches: none (batch 미결정)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req → suggest-batch-reqs → draft-batch
#### Validation Plan
- pytest: portfolio_id UUID 형식 검증, FK 고아 레코드 0건 확인, (owner_user_id, name) UNIQUE 제약 동작 확인, IF NOT EXISTS 재실행 안전성 확인
### CHG-20260511-02
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Approved 전환 — confirm-req 게이트 통과
#### Intent
- Delivery 입력으로 사용할 수 있도록 REQ-016을 승인 상태로 승격한다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- none
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs로 batch grouping 검토
#### Validation Plan
- confirm-req 게이트 재평가 결과: 필수 메타데이터, 구체적 Acceptance Criteria, Impacted Area, Change Log, blocker 없음 확인
### CHG-20260511-03
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-006 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-006 verification.md의 REQ-016 Acceptance Mapping이 모든 AC에 대해 test evidence를 포함하고 있으며, Blocking Issues 없음으로 구현 완료 상태로 전환한다.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-006 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-006)
#### Validation Plan
- BAT-006 verification.md AC-1~6 모두 test evidence 연결 확인 (tests/test_portfolio_uuid.py 14/14 passed, full suite 129/129 passed)
@@ -1,128 +0,0 @@
# req-010: credential_payload Fernet 암호화 및 startup validation
- Status: Implemented
- Type: Non-Functional
- Priority: High
- Owner: repository-maintainers
## Intent
- broker_credentials의 credential_payload(KIS app_key/app_secret)는 민감 데이터이므로 DB 평문 저장이 금지된다(R-1 High).
- Python cryptography 패키지의 Fernet(AES-128-CBC + HMAC-SHA256)으로 저장 전 암호화, 조회 시 복호화한다.
- CREDENTIAL_ENCRYPTION_KEY 환경변수가 없으면 서비스 기동을 거부해 운영 실수를 사전에 방지한다.
## Requirement
- 서비스 기동 시 CREDENTIAL_ENCRYPTION_KEY 환경변수(base64 인코딩 32바이트)가 존재하지 않으면 기동을 중단하고 에러 메시지를 출력해야 한다.
- broker_credentials에 credential_payload를 저장할 때 Fernet으로 암호화한 ciphertext를 저장해야 한다.
- broker_credentials에서 credential_payload를 조회할 때 Fernet으로 복호화한 JSON을 반환해야 한다.
## Acceptance Criteria
1. Given CREDENTIAL_ENCRYPTION_KEY가 설정된 환경에서 서비스 기동 시, Then 정상 기동되어야 한다.
2. Given CREDENTIAL_ENCRYPTION_KEY가 설정되지 않은 환경에서 서비스 기동 시, Then 기동이 실패하고 키 누락을 알리는 에러 메시지가 출력되어야 한다.
3. Given credential_payload를 broker_credentials에 저장한 뒤 DB를 직접 조회하면, Then 암호화된 ciphertext가 저장되어 있어야 한다(평문 app_key/app_secret 불가).
4. Given 암호화 저장된 credential_payload를 조회 API(GET /v1/user_info)로 읽으면, Then 복호화된 JSON({app_key: ..., app_secret: ...})이 반환되어야 한다.
5. Given 잘못된 키로 복호화를 시도하면, Then cryptography.fernet.InvalidToken 예외가 발생하고 500 또는 적절한 오류 응답이 반환되어야 한다.
## Impacted Area
- Docs: docs/project-structure.md (app/core/encryption.py 신규 모듈 반영 필요)
- Modules: app/core/config.py (CREDENTIAL_ENCRYPTION_KEY 설정 필드 및 startup validation 추가), app/core/security.py 또는 app/core/encryption.py (Fernet 암복호화 유틸리티 신규), app/repositories/postgres.py (broker_credentials 저장·조회 시 암복호화 적용)
- Tests: tests/test_auth.py 또는 신규 테스트에서 startup validation 및 암복호화 검증
## Notes
- CREDENTIAL_ENCRYPTION_KEY는 `from cryptography.fernet import Fernet; Fernet.generate_key()`로 생성한 base64 인코딩 키를 사용한다.
- startup validation은 FastAPI lifespan 이벤트 또는 Settings 초기화 시 수행한다. 설계 단계에서 위치를 확정한다.
- 키 로테이션 절차는 이번 반복 Out of Scope이다.
- Source Discovery: dcy-002 (NFR-3, R-1, OQ-3 Answered)
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-001 re-verified verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-001 verification.md (re-verified, 2026-05-03) AC-1~5 모두 PASS. Blocking Issues 없음. AC3 DB 미확인은 REQ-009와 동일 제약으로 non-blocker 처리.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-001 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-001)
#### Validation Plan
- BAT-001 verification.md (re-verified) AC-1~5 evidence 연결 확인 완료
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-010 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 5개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### 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: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-010 최초 생성 (source Discovery: dcy-002 NFR-3)
#### Intent
- credential_payload 암호화 및 startup validation 요구사항을 독립 Non-Functional REQ로 승격 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/project-structure.md (encryption 모듈 반영 필요)
- Modules:
- app/core/config.py startup validation 추가
- app/core/encryption.py 신규
- Tests:
- startup validation 및 암복호화 테스트
#### 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-007(GET /v1/user_info), REQ-009(broker_credentials 테이블)과 함께 delivery 단위 구성 권장.
@@ -1,147 +0,0 @@
# req-001: 로컬 DB 초기화 및 마이그레이션 검증 시나리오 정의
- Status: Implemented
- Type: Testing
- Priority: High
- Owner: repository-maintainers
## Intent
- 이 요구사항은 로컬 docker compose 기반 PostgreSQL 또는 TimescaleDB 환경에서 init-db, migrate-db, health-check, 최소 smoke flow를 반복 가능하게 검증하기 위한 기준선을 정의한다.
- 운영자와 유지보수 담당자가 같은 명령 순서와 같은 증거 형식으로 DB 준비 결과를 비교할 수 있도록 검증 절차를 명시하는 것이 목표다.
## Requirement
- 프로젝트는 로컬 docker compose 기반 DB 환경에 대해 fresh DB 시나리오와 app runtime이 일부 schema를 먼저 만든 시나리오를 포함한 검증 매트릭스를 정의해야 하며, init-db, migrate-db, health-check, 최소 인증 또는 portfolio smoke flow의 실행 순서, 기대 결과, 실패 조건, 증거 수집 방식을 함께 제공해야 한다.
## Acceptance Criteria
1. Given 로컬 docker compose 기반 DB와 필요한 환경 변수가 준비되어 있을 때, When 운영자가 init-db, health-check, 최소 인증 또는 portfolio smoke flow를 실행하면, Then 명령 순서, 기대 출력, pass or fail 기준, 남겨야 할 로그가 문서로 정의되어 있어야 한다.
2. Given fresh DB와 app runtime이 일부 schema를 먼저 만든 DB 시나리오가 있을 때, When 운영자가 migrate-db를 실행하면, Then 각 시나리오별 기대 결과와 실패 조건, 확인해야 할 증거가 구분되어 문서화되어 있어야 한다.
3. Given 동일한 로컬 검증 절차를 반복 실행할 때, When 운영자가 같은 명령 세트를 다시 실행하면, Then 결과 비교가 가능하도록 출력 형식과 로그 수집 위치가 일관되게 정의되어 있어야 한다.
## Impacted Area
- Docs: README.md, docs/runtime-flows.md, docs/discovery/dcy-001_20260430_real-db-migration-validation-and-operations-readiness.md
- Modules: docker-compose.local-test.yml, app/cli/commands/health.py, app/db/migrations.py
- Tests: 로컬 docker compose 기반 수동 검증 체크리스트 또는 동등한 smoke validation 절차
## Notes
- 이 요구사항은 dcy-001의 FR-1, NFR-1, NFR-2, S-1을 주로 구현 가능한 backlog로 정규화한 것이다.
- shared dev DB, staging, production-like 환경 검증은 이번 요구사항 범위에 포함하지 않는다.
- 성공 지표의 세부 artifact 형식은 REQ 승인 또는 batch planning 단계에서 확정할 수 있다.
## Change Log
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
#### Change Summary
- Implemented 전환 — BAT-007 verification passed, confirm-req-implemented 게이트 통과
#### Intent
- BAT-007 verification.md AC-1~3 모두 PASS. Blocking Issues 없음. README.md "Local DB Validation Runbook", docs/runtime-flows.md "Flow: Local DB Initialization and Validation" evidence 연결 확인 완료.
#### Acceptance Criteria Delta
- none
#### Impacted Area Delta
- none
#### Delivery Trace Delta
- Batches: BAT-007 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: implemented
- Follow-up Action: draft-release (BAT-007)
#### Validation Plan
- BAT-007 verification.md AC-1~3 evidence 연결 확인 완료
### CHG-20260430-02
- Date: 2026-04-30
- Author: GitHub Copilot
#### Change Summary
- confirm-req 승인 게이트를 통과해 이 REQ를 Approved로 전환했다.
#### Intent
- dcy-001의 delivery input으로 사용할 수 있는 확정 requirement로 승격하기 위해 상태를 변경했다.
#### Acceptance Criteria Delta
- Added:
- none
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- docs/srs/index.md
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: approved
- Follow-up Action: suggest-batch-reqs 또는 draft-batch 단계에서 REQ-001의 검증 절차를 delivery scope로 연결한다.
#### Validation Plan
- REQ 본문 필수 섹션, Acceptance Criteria 구체성, docs/srs/index.md 상태 동기화를 확인한다.
### CHG-20260430-01
- Date: 2026-04-30
- Author: GitHub Copilot
#### Change Summary
- dcy-001에서 로컬 DB 검증 시나리오 요구사항을 신규 Proposed REQ로 생성했다.
#### Intent
- 로컬 DB 준비와 smoke validation 절차를 독립 구현 및 독립 검증 가능한 Testing 단위로 분리하기 위해 생성했다.
#### Acceptance Criteria Delta
- Added:
- 로컬 docker compose 기반 init-db, health-check, smoke flow의 검증 기준 정의
- fresh DB와 runtime-precreated schema 시나리오별 migrate-db 검증 기준 정의
- 반복 실행 가능한 증거 수집 형식 정의
- Updated:
- none
- Removed:
- none
#### Impacted Area Delta
- Docs:
- README.md
- docs/runtime-flows.md
- docs/discovery/dcy-001_20260430_real-db-migration-validation-and-operations-readiness.md
- Modules:
- docker-compose.local-test.yml
- app/cli/commands/health.py
- app/db/migrations.py
- Tests:
- 로컬 DB 수동 검증 체크리스트 또는 동등한 smoke validation 절차
#### 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 단계에서 검증 매트릭스 초안의 범위와 증거 형식을 승인한다.
#### Validation Plan
- confirm-req 전까지 Acceptance Criteria가 dcy-001의 FR-1 및 S-1과 일치하는지 검토하고, 이후 batch 단계에서 실제 검증 절차와 로그 수집 artifact를 작성한다.
+10 -31
View File
@@ -74,43 +74,22 @@
## Next Requirement ID
- Current: `REQ-030`
- Current: `REQ-009`
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
## Requirement Register
| ID | Title | Type | Status | Priority | Owner | Link |
| --- | --- | --- | --- | --- | --- | --- |
| REQ-001 | 로컬 DB 초기화 및 마이그레이션 검증 시나리오 정의 | Testing | Implemented | High | repository-maintainers | [REQ-001](./Testing/req-001_local-db-validation-scenarios.md) |
| REQ-002 | Alembic와 runtime schema ownership 경계 정리 | Migration | Implemented | High | repository-maintainers | [REQ-002](./Migration/req-002_alembic-runtime-schema-ownership.md) |
| REQ-003 | 로컬 DB 운영 runbook 및 baseline 문서 갱신 | Documentation | Implemented | Medium | repository-maintainers | [REQ-003](./Documentation/req-003_local-db-operations-runbook-and-baseline-updates.md) |
| REQ-004 | GET /health 응답 스키마 확장 | Interface | Implemented | High | repository-maintainers | [REQ-004](./Interface/req-004_health-response-schema-extension.md) |
| REQ-005 | service-token 발급 시 runtime_id 동시 생성 | Interface | Approved | High | repository-maintainers | [REQ-005](./Interface/req-005_service-token-runtime-id-issuance.md) |
| REQ-006 | GET /v1/assets 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-006](./Interface/req-006_v1-assets-endpoint.md) |
| REQ-007 | GET /v1/user_info 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-007](./Interface/req-007_v1-user-info-endpoint.md) |
| REQ-008 | POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-008](./Interface/req-008_v1-ledger-trades-endpoint.md) |
| REQ-009 | v1 API 지원을 위한 DB 스키마 migration 추가 | Migration | Approved | High | repository-maintainers | [REQ-009](./Migration/req-009_v1-api-db-schema-migration.md) |
| REQ-010 | credential_payload Fernet 암호화 및 startup validation | Non-Functional | Implemented | High | repository-maintainers | [REQ-010](./Non-Functional/req-010_credential-payload-fernet-encryption.md) |
| REQ-011 | trex-basic 계약 반영을 위한 baseline 문서 갱신 | Documentation | Approved | Medium | repository-maintainers | [REQ-011](./Documentation/req-011_baseline-docs-update-for-trex-basic-contract.md) |
| REQ-012 | service-token rotate CLI 명령 구현 | Interface | Approved | Medium | repository-maintainers | [REQ-012](./Interface/req-012_service-token-rotate-cli.md) |
| REQ-013 | CLI `portfolio list --user` 필수화 및 소유자 기반 조회 | Interface | Implemented | Medium | repository-maintainers | [REQ-013](./Interface/req-013_portfolio-list-user-required.md) |
| REQ-014 | CLI `portfolio create --user` 필수화 및 소유자 설정 | Interface | Implemented | Medium | repository-maintainers | [REQ-014](./Interface/req-014_portfolio-create-user-required.md) |
| REQ-015 | CLI `credential` 명령 그룹 구현 (브로커 인증 정보 관리) | Interface | Implemented | Medium | repository-maintainers | [REQ-015](./Interface/req-015_broker-credential-cli.md) |
| REQ-016 | portfolios 테이블 UUID 전환 및 데이터 마이그레이션 | Migration | Implemented | High | repository-maintainers | [REQ-016](./Migration/req-016_portfolios-uuid-schema-migration.md) |
| REQ-017 | CLI portfolio 명령 — name+user 조합 식별 방식 전환 | Interface | Implemented | High | repository-maintainers | [REQ-017](./Interface/req-017_portfolio-cli-name-user-identification.md) |
| REQ-018 | HTTP API portfolio path parameter UUID 전환 및 v1 asset_id UUID 반환 | Interface | Implemented | High | repository-maintainers | [REQ-018](./Interface/req-018_portfolio-http-api-uuid-path-param.md) |
| REQ-019 | portfolio UUID 전환 관련 baseline 문서 갱신 | Documentation | Implemented | Medium | repository-maintainers | [REQ-019](./Documentation/req-019_portfolio-uuid-baseline-docs-update.md) |
| REQ-020 | 실배포 admin bootstrap 및 steady-state operator access runbook | Deployment | Approved | High | repository-maintainers | [REQ-020](./Deployment/req-020_production-admin-bootstrap-and-operator-access-runbook.md) |
| REQ-021 | release 검증 credential matrix 및 token provenance 운영 절차 | Documentation | Approved | High | repository-maintainers | [REQ-021](./Documentation/req-021_release-credential-matrix-and-token-provenance-procedures.md) |
| REQ-022 | 운영 로그 접근 및 release evidence 기록 절차 | Documentation | Approved | Medium | repository-maintainers | [REQ-022](./Documentation/req-022_operator-log-access-and-release-evidence-recording.md) |
| REQ-023 | Daemon Registry 관리 경로 | Interface | Approved | High | repository-maintainers | [REQ-023](./Interface/req-023_daemon-registry-and-service-token-daemon-binding.md) |
| REQ-028 | README.md Daemon Provisioning Runbook 반영 | Documentation | Approved | Medium | repository-maintainers | [REQ-028](./Documentation/req-028_daemon-model-baseline-docs-update.md) |
| REQ-029 | CLI `portfolio list` 출력 — 테두리 없는 리스트 형식 | Interface | Approved | Low | repository-maintainers | [REQ-029](./Interface/req-029_portfolio-list-borderless-output.md) |
| REQ-001 | tags 마스터 테이블 도입 | Data | Proposed | High | Justin Ko | [req-001](./Data/req-001_tags-master-table.md) |
| REQ-002 | assets.tag_ids UUID[] 컬럼 추가 | Data | Proposed | High | Justin Ko | [req-002](./Data/req-002_assets-tag-ids-column.md) |
| REQ-003 | portfolios.tag_ids UUID[] 컬럼 추가 및 portfolio_tags 테이블 제거 | Data | Proposed | High | Justin Ko | [req-003](./Data/req-003_portfolios-tag-ids-and-portfolio-tags-removal.md) |
| REQ-004 | tags 마스터 HTTP API | Interface | Proposed | High | Justin Ko | [req-004](./Interface/req-004_tags-http-api.md) |
| REQ-005 | 태그 적용·해제 HTTP API | Interface | Proposed | High | Justin Ko | [req-005](./Interface/req-005_tag-apply-remove-http-api.md) |
| REQ-006 | portfolio_tags 데이터 마이그레이션 | Migration | Proposed | High | Justin Ko | [req-006](./Migration/req-006_portfolio-tags-data-migration.md) |
| REQ-007 | pfhub tag CLI 명령어 | Interface | Proposed | Medium | Justin Ko | [req-007](./Interface/req-007_pfhub-tag-cli.md) |
| REQ-008 | 태그 모델 도입 baseline 문서 갱신 | Documentation | Proposed | Low | Justin Ko | [req-008](./Documentation/req-008_tag-baseline-docs-update.md) |
## Recent Change Log Summary
- 2026-05-18: REQ-029 Approved 생성 — `portfolio list` 테두리 없는 리스트 형식, minor-change fast path, 구현 완료
- 2026-05-18: REQ-023 CHG-20260518-01 — AC-5(`portfolio update --daemon-id`), AC-6(`portfolio list` daemon-id 표시) 추가, Impacted Area 보강, Status Approved 유지
- 2026-05-18: REQ-008 CHG-20260518-01 — `insert_ledger_trade` postgres 미구현 결함 수정, HTTP 500 정상화, Status Approved 유지
- 2026-05-18: REQ-017 CHG-20260518-01 — `get_portfolio_by_name_and_user` postgres 미구현 결함 수정, Status Implemented 유지
- 2026-05-13: REQ-023, REQ-028 Approved 전환 — confirm-req 게이트 통과 (필수 필드, AC, blocker 없음)
- 2026-05-19: REQ-001~008 최초 생성 — dcy-002 (태그 소유권 및 적용 대상 정책) confirm-discovery 후 draft-req 실행