Add ProofLine criteria documentation and Strategy Catalog v1 implementation
- Add acceptance criteria documentation (AC-0013 through AC-0023) - Add Line-0003 discovery documentation - Implement Strategy Catalog v1 with new database migration - Update portfolio API endpoints and schemas - Refactor strategy and portfolio services - Add strategy management CLI commands - Update domain models with strategy catalog support - Add comprehensive tests for v1 strategies - Update design documentation (data model, interfaces, runtime flows)
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
---
|
||||
id: "dcy-0003"
|
||||
status: confirmed
|
||||
---
|
||||
|
||||
# 전략 API v1 이전 및 관리자 전역 카탈로그 권한 분리
|
||||
|
||||
## Problem
|
||||
|
||||
`strategy_sets`는 전역 공유 카탈로그이지만 비버전 `/strategy-sets` 쓰기 API와 포트폴리오의 `get_or_create_strategy_set()` 간접 생성 경로가 일반 `trade-write` 사용자에게 열려 있었다. 전략 정의의 관리자 관리 책임, 일반 사용자의 기존 active 전략 선택 책임, 공유 설정 저장 시점과 downstream 실행 슬롯 적용 시점이 분리되어야 한다.
|
||||
|
||||
본 문서는 이미 요청자 승인을 받은 GitHub 이슈 #26을 ProofLine에 사후 포착한다. 아래 `Scope`의 **승인된 이슈 원문 전체**는 구조적 heading만 canonical H2 충돌을 피하도록 한 단계 낮췄으며, 원문 비어 있지 않은 모든 행을 순서대로 보존한다.
|
||||
|
||||
## Evidence
|
||||
|
||||
### 원문·승인·사후 문서화 경계
|
||||
|
||||
- 원문: https://github.com/fourblocks2020/portfolio-hub/issues/26
|
||||
- 원문 제목: `전략 API v1 이전 및 관리자 전역 카탈로그 권한 분리`
|
||||
- 원문 상태: `OPEN`
|
||||
- 원문 body SHA-256: `011ece78747cac8e0c38ad07b644ffef080ea1fb7480eef77380c44724b9727e`
|
||||
- 원문 자체 승인 기록: `APPROVED`, 승인자 `genichin`, 승인 시각 `2026-08-14T08:14:31Z`, unresolved Open Questions 없음.
|
||||
- 요청자는 이슈 #26 본문을 최대한 포함한 `confirmed` Discovery와 `approved` REQ, 구현 상태를 반영한 `active` AC를 새 Line에 사후 작성하도록 명시했다.
|
||||
- 이 Line은 이미 수행된 로컬 구현을 승인된 지속 사양으로 포착한다. #26 구현이 ProofLine 정식 시퀀스로 수행됐다는 주장, ProofLine implementation/delivery closure, push, PR, 원격 main 반영, release 또는 deployment 완료를 소급 생성하지 않는다.
|
||||
|
||||
### 현재 구현·검증 증거
|
||||
|
||||
- 구현 기준: `main` `5fc389dea8f05c6f711ecfe874f055979af5e58d`
|
||||
- 구현 형태: 기준 HEAD 위 로컬 `main` working tree의 미커밋 product/test/doc 변경.
|
||||
- 구현 진행 댓글: https://github.com/fourblocks2020/portfolio-hub/issues/26#issuecomment-5291151212
|
||||
- 구현 완료 댓글: https://github.com/fourblocks2020/portfolio-hub/issues/26#issuecomment-5291927258
|
||||
- 댓글이 기록한 최종 검증: 이슈 집중 회귀 `99 passed`; 실제 PostgreSQL 동시성·migration 통합 `4 passed`; 관련 회귀 `75 passed`; 알려진 기존 외부 오염 파일 제외 광범위 회귀 `608 passed, 3 skipped`; 독립 최종 QC `PASS`.
|
||||
- 2026-08-15 현재 재검증: scoped API·migration·portfolio·CLI suite `70 passed, 4 skipped`; 별도 실제 PostgreSQL 동시성·migration integration `4 passed`.
|
||||
- 두 댓글의 exact body는 이 Line의 `evidence/implementation-progress.md`와 `evidence/implementation-complete.md`에 비정식 증적으로 보존한다.
|
||||
- 위 결과는 source-time 구현 증거다. `active` AC는 normative 상태이며, 그 자체가 현재 commit-bound satisfaction이나 ProofLine delivery closure를 뜻하지 않는다.
|
||||
|
||||
## Scope
|
||||
|
||||
### 승인된 이슈 원문 전체
|
||||
|
||||
### 배경
|
||||
|
||||
현재 전략 설정 CRUD는 비버전 경로인 `/strategy-sets`에 있고, 포트폴리오·자산·태그·데몬 등 주요 제품 API는 `/v1` 아래에 있다. `strategy_sets`는 사용자 소유 리소스가 아닌 전역 공유 카탈로그지만, 현재 쓰기 API는 `trade-write` 권한을 허용한다.
|
||||
|
||||
`/v1/portfolios`의 생성·수정 요청은 `strategy_name`과 `strategy_params`를 받아 `get_or_create_strategy_set()`을 호출하므로, 전략 관리 API만 제한하면 일반 사용자의 간접 생성 경로가 남는다.
|
||||
|
||||
### 확인 근거
|
||||
|
||||
- `app/api/strategy_sets.py`: 비버전 `/strategy-sets`와 `trade-write` 쓰기 권한
|
||||
- `app/api/v1_portfolios.py`: 생성·수정 시 `get_or_create_strategy_set()`을 통한 간접 생성
|
||||
- `app/repositories/postgres.py`: 전역 `strategy_sets` 저장과 포트폴리오 참조·삭제 방어
|
||||
- `app/schemas/v1.py`: 포트폴리오 요청의 `strategy_name`·`strategy_params`, 응답의 `active_strategy_id`·`strategy_name`·`strategy_params`
|
||||
- `app/cli/commands/strategy.py`: 운영자 CLI의 직접 저장소 관리 경로
|
||||
- 워크스페이스 소비자 조사: trex-core와 전략 daemon은 `/v1/portfolios` 응답의 `strategy_name`·`strategy_params`를 소비한다. `/strategy-sets`를 직접 호출하는 다른 repository 코드는 발견되지 않았다.
|
||||
|
||||
### 문제
|
||||
|
||||
1. 전략 API만 비버전 경로에 남아 있어 API 계약 체계가 일관되지 않는다.
|
||||
2. 일반 `trade-write` 사용자가 전역 전략 설정을 생성·수정·삭제할 수 있다.
|
||||
3. 포트폴리오 생성·수정 API를 통한 전략 설정의 간접 생성이 가능하다.
|
||||
4. 전략 정의 관리와 일반 사용자의 기존 전략 선택 책임이 분리되어 있지 않다.
|
||||
5. 공유 설정 변경의 저장 시점과 trex-core 실행 슬롯의 실제 적용 시점이 구분되어 있지 않다.
|
||||
|
||||
### 승인된 범위
|
||||
|
||||
#### 읽기·선택 API
|
||||
|
||||
- 일반 사용자와 admin은 `GET /v1/strategies` 및 상세 조회 API로 active 전략을 조회할 수 있다.
|
||||
- 일반 사용자는 자기 포트폴리오 생성·수정 시 기존 active `strategy_id`만 선택할 수 있다.
|
||||
- 생성 시 `strategy_id`는 필수다.
|
||||
- 전역 active 카탈로그가 선택 가능 여부의 권위이며, legacy `avabile_strategies`는 신규 API의 선택 권한 근거로 사용하지 않는다.
|
||||
|
||||
#### 관리자 카탈로그 API
|
||||
|
||||
- `GET /v1/admin/strategies`
|
||||
- `POST /v1/admin/strategies`
|
||||
- `PATCH /v1/admin/strategies/{strategy_id}`
|
||||
- `DELETE /v1/admin/strategies/{strategy_id}`
|
||||
- 생성·수정·삭제는 admin만 허용한다.
|
||||
- admin 조회는 active와 inactive 전략을 모두 다룰 수 있다.
|
||||
|
||||
#### 공유 설정과 revision
|
||||
|
||||
- 전략은 안정적인 `strategy_id`로 식별되는 전역 공유 설정이다.
|
||||
- admin PATCH는 기존 row의 `name/config`를 제자리 수정하고 동일 ID의 `revision`을 원자적으로 증가시킨다.
|
||||
- 같은 ID를 참조하는 모든 포트폴리오의 이후 portfolio-hub API 조회에는 commit된 최신 config/revision이 반영된다.
|
||||
- 실행 중 trex-core 슬롯에는 즉시 적용하지 않는다. trex-core가 재시작되거나 하루 한 번 정해진 시각에 슬롯을 갱신할 때 최신 revision을 가져와 그때부터 적용한다.
|
||||
- 이미 생성된 슬롯·제출된 주문·완료된 거래에는 소급 적용하지 않는다.
|
||||
- portfolio-hub는 downstream daemon을 강제로 재시작하거나 설정을 push하지 않는다.
|
||||
|
||||
#### 상태·삭제
|
||||
|
||||
- 전략에 `active/inactive` 상태를 둔다.
|
||||
- 일반 조회와 신규 포트폴리오 선택에는 active 전략만 노출·허용한다.
|
||||
- 기존 포트폴리오가 참조하는 전략을 inactive로 바꿔도 참조는 보존한다.
|
||||
- DELETE는 어떤 포트폴리오에서도 참조하지 않는 전략만 hard delete한다.
|
||||
- 참조 중인 전략 DELETE는 HTTP 409를 반환한다. 신규 선택을 막는 retirement는 PATCH inactive로 처리한다.
|
||||
|
||||
#### 포트폴리오 계약과 호환성
|
||||
|
||||
- `/v1/portfolios`와 legacy `/portfolios`의 생성·수정 모두 ID-only 선택으로 통일한다.
|
||||
- 요청의 `strategy_name`·`strategy_params`는 제거하며 전달 시 HTTP 422로 거부한다.
|
||||
- 응답의 `active_strategy_id`·`strategy_name`·`strategy_params`는 trex-core 및 전략 daemon 소비 계약이므로 유지한다.
|
||||
- 비버전 `/strategy-sets` 경로는 새 `/v1` API 도입과 동시에 제거하며 deprecated alias를 유지하지 않는다.
|
||||
- 운영자 CLI의 직접 저장소 쓰기는 신뢰된 운영자 전용 관리 경로로 유지하고 일반 사용자 API로 간주하지 않는다. HTTP와 동일한 상태·참조 무결성을 우회해서는 안 된다.
|
||||
|
||||
#### 기본 전략 migration
|
||||
|
||||
- Alembic data migration은 `name="target_value"`, `config={}`, `active`인 기본 전략을 idempotent하게 생성한다.
|
||||
- 동일 `(name, config)` row가 이미 존재하면 해당 row를 재사용한다.
|
||||
- 기본 전략이 존재하더라도 포트폴리오 생성 요청의 `strategy_id` 필수 조건은 완화하지 않는다. 호출자는 카탈로그 조회로 ID를 얻어 명시한다.
|
||||
|
||||
#### 오류 계약
|
||||
|
||||
- 미존재 `strategy_id`: HTTP 404
|
||||
- inactive 전략 신규 선택: HTTP 409
|
||||
- 제거된 `strategy_name`·`strategy_params` 또는 malformed 입력: HTTP 422
|
||||
- 권한 부족: HTTP 403
|
||||
- 참조 중 전략 DELETE: HTTP 409
|
||||
|
||||
### 승인된 결정 사항
|
||||
|
||||
1. 외부 API 명칭은 `/v1/strategies`, `/v1/admin/strategies`를 사용한다.
|
||||
2. `active/inactive` 상태를 도입하고 inactive 참조는 보존하되 신규 선택은 금지한다.
|
||||
3. PATCH는 기존 ID의 공유 설정을 제자리 수정하여 모든 참조 포트폴리오의 이후 API 조회에 반영한다.
|
||||
4. 포트폴리오는 active `strategy_id`를 필수로 명시하고 전역 active 카탈로그를 선택 권위로 사용한다.
|
||||
5. 포트폴리오 요청의 `strategy_name`·`strategy_params`는 제거하고 422로 거부한다.
|
||||
6. 비버전 `/strategy-sets`는 새 API 도입과 동시에 제거한다.
|
||||
7. 기본 `target_value`/`{}` 전략은 Alembic migration으로 idempotent하게 생성한다.
|
||||
8. legacy `/portfolios`도 ID-only로 통일하고 CLI는 신뢰된 운영자 경로로 유지한다.
|
||||
9. portfolio-hub 저장은 즉시 보이지만 실행 슬롯 적용은 trex-core 슬롯 재생성·정기 갱신 시점으로 지연한다.
|
||||
10. 기본 전략은 active 상태로 생성하거나 기존 동일 row를 재사용한다.
|
||||
11. 미존재 404, inactive 409, legacy/malformed 422, 권한 부족 403으로 구분한다.
|
||||
12. 참조 중 전략은 삭제하지 않고 409를 반환하며 retirement는 inactive로 처리한다.
|
||||
|
||||
### 승인된 수용 기준
|
||||
|
||||
- [ ] 일반 사용자 토큰으로 active 전략 카탈로그 목록·상세를 조회할 수 있다.
|
||||
- [ ] 일반 사용자 토큰으로 전략 설정 생성·수정·삭제 시 HTTP 403을 반환한다.
|
||||
- [ ] admin 토큰으로 전략 설정 생성·조회·수정·상태 변경·미참조 삭제가 가능하다.
|
||||
- [ ] admin이 공유 설정을 수정하면 ID는 유지되고 revision이 증가하며 모든 참조 포트폴리오의 이후 API 조회에 최신 설정이 나타난다.
|
||||
- [ ] inactive 전략은 일반 카탈로그와 신규 선택에서 제외되지만 기존 포트폴리오 참조는 보존된다.
|
||||
- [ ] 포트폴리오 생성 시 active `strategy_id`가 필수이며 수정 시에도 active ID만 허용한다.
|
||||
- [ ] `/v1/portfolios`와 legacy `/portfolios`가 새로운 전략 설정을 간접 생성하지 않는다.
|
||||
- [ ] 포트폴리오 요청의 `strategy_name`·`strategy_params`는 HTTP 422로 거부하지만 응답의 전략 필드는 유지된다.
|
||||
- [ ] 미존재·inactive·권한 부족·malformed 요청이 승인된 HTTP 상태 코드로 구분된다.
|
||||
- [ ] 새 API 도입 시 비버전 `/strategy-sets` 경로가 제거되고 관련 문서·테스트가 갱신된다.
|
||||
- [ ] migration이 기본 active `target_value`/`{}` 전략을 중복 없이 생성 또는 재사용한다.
|
||||
- [ ] 참조 중 전략 DELETE는 409이며 포트폴리오 참조를 자동 이전하거나 삭제하지 않는다.
|
||||
- [ ] trex-core 슬롯은 설정 PATCH만으로 즉시 변경되지 않고 다음 슬롯 재생성·정기 갱신에서 최신 revision을 반영한다.
|
||||
- [ ] 권한·간접 생성·상태·revision·오류·삭제·migration·호환성에 대한 회귀 테스트가 추가된다.
|
||||
|
||||
### 범위 밖
|
||||
|
||||
- trex-core의 정확한 일일 슬롯 갱신 시각 및 scheduler 구현 변경
|
||||
- portfolio-hub가 downstream daemon을 강제로 reload·restart하거나 설정을 push하는 기능
|
||||
- 이미 생성된 슬롯·제출된 주문·완료된 거래의 소급 재해석
|
||||
- 내부 DB 테이블명 `strategy_sets` 변경
|
||||
- 운영 배포·데이터 migration 실행·release 승인
|
||||
|
||||
### 위험 및 의존성
|
||||
|
||||
- `/strategy-sets` 즉시 제거는 breaking change다. 현재 워크스페이스에서 외부 코드 소비자는 발견되지 않았지만 문서와 운영 호출자는 배포 전에 새 경로로 전환해야 한다.
|
||||
- 공유 config는 mutable하므로 revision 증가와 원자적 update가 필요하다.
|
||||
- trex-core의 실제 반영은 슬롯 refresh 계약에 의존한다. #26은 최신 revision을 제공하는 portfolio-hub 경계까지만 소유한다.
|
||||
- legacy 요청 필드가 framework 기본 동작으로 조용히 무시되지 않도록 명시적으로 422를 보장해야 한다.
|
||||
|
||||
### Approval State
|
||||
|
||||
- **Status:** APPROVED
|
||||
- **Approved by:** genichin
|
||||
- **Approved at:** 2026-08-14T08:14:31Z
|
||||
- **Approval basis:** 이슈 #26의 OQ-001~OQ-012 단일 선택 완료와 요청자의 명시적 Discovery 승인 지시
|
||||
- **Unresolved Open Questions:** 없음
|
||||
- **Authority boundary:** 이 승인은 위 Discovery 범위를 승인하며 구현 완료, main 반영, 배포 또는 release 승인을 의미하지 않는다.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
### 사후 ProofLine 경계
|
||||
|
||||
- 이 사후 사양 포착은 구현이 ProofLine 정식 시퀀스로 수행됐다는 주장이나 implementation/delivery/release/deployment lifecycle closure를 만들지 않는다.
|
||||
- 구현 진행·완료 댓글은 canonical lifecycle artifact가 아니라 출처가 명시된 비정식 evidence다.
|
||||
- 현재 구현은 미커밋 working tree에 있으므로 exact implementation commit, 원격 main 통합, push, PR, 게시, 운영 배포 및 release는 확인되거나 승인된 것으로 간주하지 않는다.
|
||||
- 이슈 원문의 `범위 밖` 항목과 authority boundary는 위 승인 원문 전체에 보존되며 `req-0003` 및 연결 AC가 이를 축소하거나 확장하지 않는다.
|
||||
@@ -0,0 +1,61 @@
|
||||
# 구현 완료 — GitHub Issue #26 비정식 증적
|
||||
|
||||
> 이 문서는 ProofLine canonical lifecycle artifact가 아니다. #26 구현 당시 GitHub 댓글을 출처와 함께 원문 보존한 비정식 증적이다.
|
||||
> 이 증거는 로컬 구현·검증 상태를 기록하지만 ProofLine 정식 implementation chronology, delivery closure, push, PR, 원격 main 반영, release 또는 deployment 완료를 소급 증명하지 않는다.
|
||||
> Source: https://github.com/fourblocks2020/portfolio-hub/issues/26#issuecomment-5291927258
|
||||
> Comment ID: `IC_kwDOR4l8PM8AAAABO2xm2g`
|
||||
> Author: `genichin`
|
||||
> Created: `2026-08-14T09:48:42Z`
|
||||
> Original body SHA-256: `c3bc9a3ad6c9cc6678ed700a11add03b1f5abe71645a21e157dc091faec28f10`
|
||||
> Original body boundary: 아래 `---` 다음 byte부터 파일 끝까지가 GitHub 원문 body다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 완료
|
||||
|
||||
이슈 #26 **전략 API v1 이전 및 관리자 전역 카탈로그 권한 분리**의 승인 범위를 로컬 `main` 작업 트리에 구현하고 실제 PostgreSQL 검증 및 독립 QC까지 완료했습니다.
|
||||
|
||||
### 구현 내용
|
||||
|
||||
- 비버전 `/strategy-sets` 제거 및 전략 API `/v1` 이전
|
||||
- 일반 사용자: active 전략 조회 전용 `/v1/strategies`
|
||||
- 관리자: 전체 전략 카탈로그 CRUD `/v1/admin/strategies`
|
||||
- 전략 카탈로그에 `active/inactive` 상태와 revision 도입
|
||||
- 신규 전략 revision 1, 실질적 PATCH만 revision 증가, no-op 유지
|
||||
- 동시 PATCH를 `FOR UPDATE`로 직렬화하고 최신 name/config 기준으로 derived `nick` 계산
|
||||
- 포트폴리오 create/update에서 기존 active `strategy_id`만 선택
|
||||
- `strategy_name`·`strategy_params`를 통한 간접 전략 생성 차단
|
||||
- 전략 선택의 `FOR SHARE`와 포트폴리오 저장을 같은 트랜잭션으로 묶어 inactivate/delete race 차단
|
||||
- 전략 DELETE의 `FOR UPDATE`·두 참조 형태 검사·삭제를 한 트랜잭션으로 통합
|
||||
- stale BUY/SELL snapshot이 전략 선택 컬럼을 덮어쓰지 못하도록 거래 저장 책임 분리
|
||||
- 거래 portfolio UPDATE와 ledger INSERT 원자화
|
||||
- 포트폴리오 응답에 최신 `strategy_revision` 노출
|
||||
- 기본 `target_value`/`{}` 전략 migration과 runtime nick 파생 규칙 통일
|
||||
- CLI, 문서, migration 및 동시성 회귀 테스트 갱신
|
||||
|
||||
### 실제 검증 결과
|
||||
|
||||
- 이슈 집중 회귀: `99 passed`
|
||||
- 실제 PostgreSQL 동시성·migration 통합 테스트: 최종 `4 passed`
|
||||
- 동시 partial PATCH의 nick/revision 일관성
|
||||
- 전략 선택 대 DELETE race의 orphan 방지
|
||||
- stale A snapshot → B 선택 → A 삭제 → stale BUY 저장 후 B 유지
|
||||
- 신규 DB 전체 Alembic upgrade 및 head 재실행
|
||||
- 관련 최종 회귀: `75 passed`
|
||||
- 알려진 기존 문제 파일 제외 광범위 회귀: `608 passed, 3 skipped`
|
||||
- 독립 최종 QC 재실행: PostgreSQL `4 passed`, focused `37 passed, 4 skipped`
|
||||
- Python compile, `git diff --check`, 변경 핵심 파일 Ruff 검사: 통과
|
||||
- 독립 QC 최종 판정: **PASS**
|
||||
|
||||
전체 broad 실행에 남은 3건은 저장소 밖 `temp/deleted_20260813/.worktrees`를 스캔하는 기존 환경 오염 실패로, 이번 변경에 의한 회귀가 아닙니다.
|
||||
|
||||
### 비차단 후속 권고
|
||||
|
||||
- PostgreSQL 통합 테스트 cleanup을 fixture/`finally` 기반으로 강화하면 테스트가 중간 실패·중단될 때의 잔여 fixture 방지를 개선할 수 있습니다. 현재 성공 경로 cleanup은 QC 전후 잔여 레코드 수가 동일해 정상입니다.
|
||||
|
||||
### 완료 경계
|
||||
|
||||
- 완료: 로컬 구현, 실제 테스트, migration 검증, 문서 갱신, 독립 QC
|
||||
- 미수행: 커밋, push, PR, 원격 `main` 반영, 이미지 게시, 운영 설정 변경, 배포, 릴리스
|
||||
|
||||
현재 변경은 기준 HEAD `5fc389dea8f05c6f711ecfe874f055979af5e58d`의 로컬 `main` 작업 트리에 **미커밋 상태**로 보존되어 있습니다. 이 댓글은 제품 구현·검증 완료를 뜻하며 배포·릴리스 또는 이슈 lifecycle closure를 뜻하지 않습니다.
|
||||
@@ -0,0 +1,65 @@
|
||||
# 구현 진행 상황 — GitHub Issue #26 비정식 증적
|
||||
|
||||
> 이 문서는 ProofLine canonical lifecycle artifact가 아니다. #26 구현 당시 GitHub 댓글을 출처와 함께 원문 보존한 비정식 증적이다.
|
||||
> 이 증거는 로컬 구현·검증 상태를 기록하지만 ProofLine 정식 implementation chronology, delivery closure, push, PR, 원격 main 반영, release 또는 deployment 완료를 소급 증명하지 않는다.
|
||||
> Source: https://github.com/fourblocks2020/portfolio-hub/issues/26#issuecomment-5291151212
|
||||
> Comment ID: `IC_kwDOR4l8PM8AAAABO2CPbA`
|
||||
> Author: `genichin`
|
||||
> Created: `2026-08-14T08:21:45Z`
|
||||
> Original body SHA-256: `539cfe82e6f8281c5821c52f484a4078b116e52f5696ee0f9ea502f27b42cfeb`
|
||||
> Original body boundary: 아래 `---` 다음 byte부터 파일 끝까지가 GitHub 원문 body다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 진행 상황
|
||||
|
||||
- **대상:** 전략 API v1 이전 및 관리자 전역 카탈로그 권한 분리
|
||||
- **브랜치:** `main`
|
||||
- **기준:** `main` (`5fc389dea8f05c6f711ecfe874f055979af5e58d`)
|
||||
- **상태:** 로컬 구현·검증·독립 QC 완료 (PASS)
|
||||
- **최종 업데이트:** 2026-08-14 18:47 KST
|
||||
|
||||
### 구현 계획 및 작업 목록
|
||||
|
||||
- [x] 전략 카탈로그 데이터 모델과 저장소 기능 확장
|
||||
- 안정적인 전략 ID를 유지하면서 `active/inactive` 상태와 원자적으로 증가하는 revision을 저장·조회하고, 동시 PATCH에서도 `name/config/nick` 일관성을 보장한다.
|
||||
- [x] `/v1/strategies` 일반 조회 API 구현
|
||||
- 일반 사용자는 active 전략의 목록과 상세만 조회할 수 있도록 한다.
|
||||
- [x] `/v1/admin/strategies` 관리자 관리 API 구현
|
||||
- admin만 전체 목록 조회, 생성, 제자리 수정·상태 변경, 미참조 전략 삭제를 수행할 수 있도록 한다.
|
||||
- [x] 포트폴리오 전략 선택을 기존 active `strategy_id` 전용으로 변경
|
||||
- `/v1/portfolios`와 legacy `/portfolios` 모두 생성 시 전략 ID를 필수로 받고, 수정 시에도 기존 active 전략만 선택하며 선택·저장·삭제 race를 DB 트랜잭션과 row lock으로 차단한다.
|
||||
- [x] 동시성·참조 무결성 회귀 검증
|
||||
- 동시 전략 PATCH와 포트폴리오 선택·전략 삭제 경합에서도 derived `nick`, revision, active 선택, orphan 방지가 유지되는지 실제 PostgreSQL로 검증한다.
|
||||
- [x] 일반 사용자의 간접 전략 생성 및 비버전 API 제거
|
||||
- 포트폴리오 요청의 `strategy_name`·`strategy_params`를 422로 거부하고 비버전 `/strategy-sets` 라우트를 제거한다.
|
||||
- [x] 기본 전략 데이터 migration과 운영자 CLI 무결성 반영
|
||||
- 기본 active `target_value`/`{}` 전략을 중복 없이 생성·재사용하고, CLI가 상태·revision·참조 무결성을 우회하지 않도록 한다.
|
||||
- [x] 권한·상태·revision·오류·삭제·호환성 회귀 테스트 추가 및 실행
|
||||
- 승인된 HTTP 403/404/409/422 계약, 참조 보존, 간접 생성 차단과 migration idempotency를 실제 테스트로 검증한다.
|
||||
- [x] 인터페이스·런타임·데이터 모델 문서 갱신 및 전체 변경 검수
|
||||
- 새 API 계약과 비버전 경로 제거, downstream 적용 시점의 경계를 문서화하고 전체 diff와 회귀를 점검한다.
|
||||
|
||||
### 진행 기록
|
||||
|
||||
- 2026-08-14 17:21 KST — 이슈 #26의 승인된 범위와 수용 기준을 기준으로 구현 계획을 확정했다.
|
||||
- 2026-08-14 17:21 KST — 별도 구현 브랜치 없이 `main` (`5fc389d`)에서 구현·검증을 시작했다.
|
||||
- 2026-08-14 17:32 KST — v1 전략 API, 전략 상태·revision 저장소, ID 전용 포트폴리오 선택, 비버전 라우트 제거 및 migration의 초기 구현을 작업트리에 반영했다. 아직 검증 전이므로 체크하지 않았다.
|
||||
- 2026-08-14 17:33 KST — 첫 전체 회귀 실행 결과 `571 passed, 27 failed, 6 skipped`였다. 새 필수 `strategy_id` 계약에 맞지 않는 기존 테스트·CLI와 미완료 전용 테스트·문서를 보완한 뒤 다시 검증한다.
|
||||
- 2026-08-14 17:44 KST — CLI·기존 테스트·전용 API/migration 테스트·문서를 보완했다. 이슈 집중 회귀는 `166 passed`, 전체 제품 테스트는 기존 stale worktree 정적검사 3건을 제외하고 `606 passed, 3 skipped`였다.
|
||||
- 2026-08-14 17:49 KST — 실제 PostgreSQL 신규 DB에서 Alembic 전체 upgrade와 head 재실행을 검증했다. 기본 `target_value`/`{}` 전략은 active, revision 1로 한 건 생성됐다.
|
||||
- 2026-08-14 17:50 KST — 실제 PostgreSQL 기반 HTTP 흐름에서 admin 생성·PATCH·삭제, 일반 조회·권한 거부, ID 유지와 revision 증가를 검증했다.
|
||||
- 2026-08-14 17:51 KST — 검증 중 발견한 inactive 신규 전략이 revision 2로 시작하는 결함을 수정해 실제 DB와 회귀 테스트에서 revision 1을 확인했다.
|
||||
- 2026-08-14 17:52 KST — 독립 QC가 동시 PATCH의 derived `nick` 불일치 가능성과 전략 선택·삭제 사이의 참조 race를 HIGH로 판정해 반려했다. 특히 참조 race 수정은 포트폴리오 mutation과 전략 삭제를 같은 DB 트랜잭션/row lock 경계로 묶는 작업이 추가로 필요해, 승인된 구현 계획 보완이 필요하므로 구현을 중지했다.
|
||||
- 2026-08-14 18:17 KST — 요청자가 트랜잭션·row lock·동시성 테스트를 구현 계획에 추가하고 QC 지적 전체를 수정하도록 명시 승인해 구현을 재개했다.
|
||||
- 2026-08-14 18:34 KST — 전략 PATCH를 `FOR UPDATE` 트랜잭션으로 직렬화하고, active 전략 선택의 `FOR SHARE` lock과 포트폴리오 INSERT/UPDATE를 같은 트랜잭션으로 묶었다. 전략 DELETE도 `FOR UPDATE`·참조 검사·삭제를 한 트랜잭션으로 통합했다.
|
||||
- 2026-08-14 18:34 KST — 포트폴리오 응답에 최신 `strategy_revision`을 추가하고, migration 기본 전략 nick을 runtime SHA-1 규칙과 일치시켰다.
|
||||
- 2026-08-14 18:34 KST — 집중 회귀 `99 passed`, 실제 PostgreSQL 동시 PATCH·선택/삭제 race·신규 DB migration 재실행 통합 테스트 `3 passed`, 알려진 외부 정적 sweep 및 기존 ledger fixture 파일을 제외한 광범위 회귀 `607 passed, 3 skipped`를 확인했다. Python compile, `git diff --check`, 변경 핵심 파일 Ruff 검사도 통과했다.
|
||||
- 2026-08-14 18:34 KST — 전체 원문 실행은 `609 passed, 3 failed, 3 errors, 3 skipped`였다. 실패 3건은 저장소 밖 `temp/deleted_20260813/.worktrees`를 스캔하는 기존 정적 테스트이고, error 3건은 기존 ledger integration fixture가 현재 DB의 필수 `owner_user_id`를 넣지 않는 문제로 이번 전략 변경과 분리했다. 독립 재-QC를 요청했다.
|
||||
- 2026-08-14 18:40 KST — 독립 재-QC는 거래 저장 `save()`가 stale 포트폴리오 snapshot의 전략 A를 다시 써, B로 선택 변경 후 삭제된 A 참조를 되살릴 수 있음을 HIGH로 재현해 FAIL 판정했다. 이전 findings는 모두 해소된 것으로 확인됐다.
|
||||
- 2026-08-14 18:43 KST — `save()`에서 전략 선택 컬럼 쓰기를 제거하고 balance/price/buffer/timestamp UPDATE와 ledger INSERT를 단일 트랜잭션으로 묶었다. A snapshot→B 선택→A 삭제→stale BUY 저장 후에도 B가 유지되고 A가 없는 실제 PostgreSQL 회귀 테스트를 추가했다.
|
||||
- 2026-08-14 18:43 KST — 실제 PostgreSQL 통합 테스트 `4 passed`, 관련 회귀 `75 passed`, 알려진 기존 문제 파일 제외 광범위 회귀 `608 passed, 3 skipped`, 핵심 Ruff·compile·diff 검사를 통과해 최종 독립 재-QC를 요청했다.
|
||||
- 2026-08-14 18:47 KST — 최종 독립 재-QC가 PASS를 판정했다. stale BUY/SELL snapshot의 삭제 전략 참조 복원 문제가 완전히 해소됐고, 거래 UPDATE와 ledger INSERT의 단일 트랜잭션, selection/strategy lock 상호작용, 실제 PostgreSQL 회귀 시나리오를 확인했다. 독립 재실행은 PostgreSQL `4 passed`, focused `37 passed, 4 skipped`, compile·diff 검사 통과였다.
|
||||
- 2026-08-14 18:47 KST — 비차단 테스트 위생 권고: PostgreSQL 통합 테스트 cleanup을 향후 fixture/finally로 강화하면 테스트 중단 시 잔여 fixture를 더 확실히 방지할 수 있다. 현재 성공 경로 cleanup은 전후 잔여 레코드 수가 동일해 정상이다.
|
||||
|
||||
> 모든 체크는 로컬 작업 트리의 구현·테스트·독립 QC 완료를 뜻한다. 커밋·push·PR·main 원격 반영·게시·배포·릴리스는 수행하지 않았으며 별도 지시와 게이트가 필요하다.
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
id: "line-0003"
|
||||
---
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
id: "req-0003"
|
||||
status: approved
|
||||
discovery: "dcy-0003"
|
||||
criteria:
|
||||
create:
|
||||
- "ac-0013"
|
||||
- "ac-0014"
|
||||
- "ac-0015"
|
||||
- "ac-0016"
|
||||
- "ac-0017"
|
||||
- "ac-0018"
|
||||
- "ac-0019"
|
||||
- "ac-0020"
|
||||
- "ac-0021"
|
||||
- "ac-0022"
|
||||
- "ac-0023"
|
||||
update: []
|
||||
retire: []
|
||||
satisfy: []
|
||||
---
|
||||
|
||||
# 전략 API v1 이전 및 관리자 전역 카탈로그 권한 분리
|
||||
|
||||
## Objective
|
||||
|
||||
전역 전략 카탈로그의 일반 조회·선택과 관리자 관리를 versioned API에서 분리하고, 안정적인 전략 identity·revision·상태·참조 무결성을 유지하면서 포트폴리오의 ID-only 선택 계약과 downstream 적용 시점 경계를 제공한다.
|
||||
|
||||
## Scope
|
||||
|
||||
- 일반 사용자와 admin의 active 전략 목록·상세 조회를 `/v1/strategies`로 제공한다.
|
||||
- active·inactive 전체 전략의 생성·조회·제자리 수정·상태 변경·미참조 삭제를 `/v1/admin/strategies`의 admin 전용 권한으로 제공한다.
|
||||
- 전략의 안정적인 ID, 양의 revision, active/inactive 상태와 동시 변경 시 원자적 일관성을 보장한다.
|
||||
- `/v1/portfolios`와 legacy `/portfolios`의 생성·수정을 기존 active `strategy_id` 전용으로 통일하고 간접 전략 생성을 제거한다.
|
||||
- 요청의 제거된 전략 이름·파라미터 필드는 거부하되 기존 downstream 응답 필드는 최신 공유 설정과 revision으로 유지한다.
|
||||
- 참조 중 전략의 hard delete를 거부하고 inactive retirement 및 기존 참조 보존을 지원한다.
|
||||
- 비버전 `/strategy-sets`를 alias 없이 제거하고 운영자 CLI가 HTTP와 같은 상태·참조 무결성을 우회하지 않게 한다.
|
||||
- 기본 active `target_value`/`{}` 전략을 idempotent Alembic data migration으로 생성하거나 기존 동일 row를 재사용한다.
|
||||
- portfolio-hub의 commit된 최신 revision 제공 시점과 trex-core 슬롯의 재생성·정기 갱신 적용 시점을 구분한다.
|
||||
- 동시 PATCH, 선택·상태 변경·삭제 경합 및 stale 거래 snapshot 저장에서도 derived field, revision, 현재 참조와 ledger 원자성을 보존한다.
|
||||
|
||||
## Constraints
|
||||
|
||||
- 전역 active 카탈로그가 신규 선택 권위이며 legacy `avabile_strategies`는 선택 권한 근거가 아니다.
|
||||
- 외부 오류 계약은 미존재 `404`, inactive 신규 선택과 참조 중 삭제 `409`, 제거 필드·malformed 입력 `422`, 권한 부족 `403`으로 구분한다.
|
||||
- 기존 inactive 전략 참조는 보존하며 자동 이전하거나 삭제하지 않는다.
|
||||
- portfolio-hub는 downstream daemon을 강제 reload/restart하거나 설정을 push하지 않고 이미 생성된 슬롯·주문·완료 거래를 소급 재해석하지 않는다.
|
||||
- 내부 DB 테이블명 `strategy_sets`는 유지한다.
|
||||
|
||||
### Current Implementation Assessment
|
||||
|
||||
- 이 REQ는 요청자 지시에 따라 사후 승인 상태로 포착됐다. `approved`/`active`는 normative authority이며 ProofLine 구현 chronology나 satisfaction marker가 아니다.
|
||||
- 현재 `main` 기준 HEAD `5fc389dea8f05c6f711ecfe874f055979af5e58d` 위 미커밋 working tree에 portfolio-hub 범위의 구현·테스트·문서 변경이 존재한다.
|
||||
- 2026-08-15 현재 scoped 재실행 `uv run python -m pytest -q tests/test_v1_strategies.py tests/test_strategy_catalog_migration.py tests/test_v1_portfolios_crud.py tests/test_strategy_catalog_postgres_integration.py tests/test_cli.py`는 `70 passed, 4 skipped`였다. 이어 `PORTFOLIOHUB_RUN_POSTGRES_INTEGRATION=1 uv run python -m pytest -q tests/test_strategy_catalog_postgres_integration.py`를 실행해 실제 PostgreSQL integration `4 passed`를 확인했다.
|
||||
- `ac-0013`~`ac-0021`, `ac-0023`은 현재 working-tree 구현과 executable tests가 대응한다. 다만 `ac-0014`의 비-admin mutation별 독립 negative test, `ac-0017`의 제거 요청 필드·응답 호환 필드 전용 test, `ac-0023`의 ledger INSERT 실패 fault-injection rollback test는 `PARTIAL`이며, exact implementation commit과 ProofLine delivery closure도 없다.
|
||||
- `ac-0022`의 portfolio-hub 경계(최신 revision 조회 제공, 강제 push/reload 부재)는 구현되어 있으나, trex-core가 다음 슬롯 재생성·정기 갱신에서 최신 revision을 실제 반영한다는 cross-repository end-to-end seam은 이 저장소 증거만으로 확인되지 않아 `UNVERIFIED`다.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- trex-core의 정확한 일일 슬롯 갱신 시각 또는 scheduler 구현 변경
|
||||
- downstream daemon 강제 reload·restart 또는 설정 push
|
||||
- 기존 슬롯·주문·완료 거래의 소급 재해석
|
||||
- 내부 `strategy_sets` 테이블명 변경
|
||||
- 운영 배포, production data migration 실행, push, PR, 원격 main 반영 또는 release 승인
|
||||
- 이 사후 문서로 과거 구현에 ProofLine 정식 implementation/delivery chronology나 terminal closure를 소급 부여하는 행위
|
||||
Reference in New Issue
Block a user