docs(proofline): confirm line 0004 discovery
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
---
|
||||
id: "dcy-0004"
|
||||
status: confirmed
|
||||
---
|
||||
|
||||
# 포트폴리오 생성 시 연결 데몬 persistence 및 배정 검증
|
||||
|
||||
## Problem
|
||||
|
||||
포트폴리오 생성 요청에 선택한 `managed_by_daemon_id`가 포함되어도 `portfolio-hub`의 `POST /v1/portfolios` handler와 repository create port 사이에서 해당 값이 누락되어 persistence되지 않는다. 비-null PATCH 재지정은 전달되므로 사용자는 생성 직후 다시 편집해야 연결 데몬을 반영할 수 있다.
|
||||
|
||||
이 Discovery는 요청자가 승인한 GitHub 이슈 #27을 canonical ProofLine 문서로 포착한다. 아래 `Scope`의 **승인된 이슈 본문 전체**는 구조적 heading만 canonical H2 충돌을 피하도록 한 단계 낮췄으며, 원문의 비어 있지 않은 모든 내용과 승인 경계를 순서대로 보존한다.
|
||||
|
||||
## Evidence
|
||||
|
||||
### 승인 원문과 provenance
|
||||
|
||||
- 원문: https://github.com/fourblocks2020/portfolio-hub/issues/27
|
||||
- 원문 제목: `trex-web : 포트폴리오 추가시 연결 데몬을 설정하였음에도 지정되지 않음`
|
||||
- 원문 상태: `open`
|
||||
- 원문 최초 작성: `2026-07-21T14:42:15Z`
|
||||
- 승인 본문 최종 갱신: `2026-08-15T03:03:53Z`
|
||||
- 원문 body SHA-256: `17668fbfac2419713e8247637fbf05631d93a71bdb74db0fe122743fba8d44e4`
|
||||
- 원문 승인 상태: `APPROVED`, 승인자 `genichin`, 승인 시각 `2026-08-15T03:02:35Z`, unresolved Open Questions 없음.
|
||||
- 이슈는 최초 `fourblocks2020/trex-web#10`에서 생성됐으나 근본 원인이 `portfolio-hub`의 create persistence 경계로 확인되어 현재 `fourblocks2020/portfolio-hub#27`로 이전됐다.
|
||||
- exact 승인 본문 snapshot은 `evidence/issue-27-approved-discovery.md`에 보존한다.
|
||||
- source review 기준 commit은 `portfolio-hub` `2a1a637fb9fb19944991cb902530faf72f0e9f84`이며, 이 문서 작성 시 product 구현은 수행하지 않았다.
|
||||
|
||||
### 승인·handoff 경계
|
||||
|
||||
- 요청자는 OQ-001~OQ-004의 단일 선택을 완료하고 Discovery 검토·승인을 명시했다.
|
||||
- `trex-web` 관련 코드·타입·화면·테스트 수정은 명시적으로 Out of Scope다.
|
||||
- 이 confirmed Discovery는 다음 REQ 작성 및 검토를 위한 scope authority다. 구현 완료, approved REQ, active/satisfied AC, implementation/delivery closure, main 반영, push, PR, migration 실행, 배포 또는 release 완료를 주장하지 않는다.
|
||||
|
||||
## Scope
|
||||
|
||||
### 승인된 이슈 본문 전체
|
||||
|
||||
### 원래 제보
|
||||
|
||||
> 편집으로 다시 지정해야 반영 됨.
|
||||
|
||||
### ProofLine Discovery
|
||||
|
||||
- **Status:** APPROVED
|
||||
- **Source:** 프로젝트 보드 `trex` / 현재 `fourblocks2020/portfolio-hub#27` (최초 `fourblocks2020/trex-web#10`에서 이전)
|
||||
- **검토 기준:** `portfolio-hub` `2a1a637fb9fb19944991cb902530faf72f0e9f84`, 증상 경계 확인용 `trex-web` `b72ccd7cd639447e71d7e785857d2c503e3a1841`
|
||||
- **승인 경계:** 이 승인은 아래 `portfolio-hub` Discovery 범위를 승인한다. 구현 완료, `trex-web` 변경, main 반영, push, PR, 배포 또는 release 승인을 의미하지 않는다.
|
||||
|
||||
#### Problem
|
||||
|
||||
포트폴리오 추가 화면에서 사용자가 `연결 데몬`을 선택해 생성해도 생성된 포트폴리오의 `managed_by_daemon_id`가 저장되지 않는다. 같은 포트폴리오를 편집하여 비-null 데몬을 다시 지정하면 반영된다.
|
||||
|
||||
프런트엔드는 생성 요청에 선택값을 포함하지만, `portfolio-hub`의 `POST /v1/portfolios` 처리기가 해당 값을 저장소 생성 호출로 전달하지 않는다. 반면 `PATCH /v1/portfolios/{portfolio_id}`는 비-null `managed_by_daemon_id`를 update fields에 포함하므로 “생성에서는 누락되고 편집에서는 반영”되는 제보와 코드 경로가 일치한다.
|
||||
|
||||
#### Evidence
|
||||
|
||||
- Live issue: https://github.com/fourblocks2020/portfolio-hub/issues/27
|
||||
- 원래 제보: “편집으로 다시 지정해야 반영 됨.”
|
||||
- 최초 작성: `2026-07-21T14:42:15Z`
|
||||
- `trex-web/features/portfolio/portfolio-create-form.tsx:83-91`
|
||||
- 선택한 데몬을 `managed_by_daemon_id: selectedDaemonId || null`로 생성 mutation에 전달한다.
|
||||
- `trex-web/lib/api/portfolio-hub.ts:87-95,251-256`
|
||||
- 생성 요청 타입과 `POST /v1/portfolios` wrapper가 `managed_by_daemon_id`를 허용하고 payload를 그대로 전송한다.
|
||||
- `portfolio-hub/app/schemas/v1.py:235-258`
|
||||
- 생성·수정 schema 모두 `managed_by_daemon_id` 입력을 허용한다.
|
||||
- `portfolio-hub/app/api/v1_portfolios.py:170-183`
|
||||
- 생성 handler가 `repo.create_portfolio(...)`에 `managed_by_daemon_id`를 전달하지 않는다.
|
||||
- `portfolio-hub/app/repositories/portfolio_repository.py:150-165`
|
||||
- 현재 생성 port 시그니처에도 `managed_by_daemon_id`가 없다.
|
||||
- `portfolio-hub/app/api/v1_portfolios.py:222-235`
|
||||
- 편집 handler는 `exclude_none=True`로 얻은 비-null fields를 `repo.update_portfolio(...)`에 전달하므로 비-null 데몬 재지정은 저장된다.
|
||||
- `portfolio-hub/app/api/v1_daemons.py:26-56`
|
||||
- 일반 daemon 목록은 호출자 소유 범위이며 active와 inactive를 `is_active`로 구분해 반환한다.
|
||||
- `portfolio-hub/docs/data-model.md:231-259`
|
||||
- daemon ID는 server-generated UUID이고, daemon은 사용자 소유이며, service token 바인딩에는 존재·active·동일 소유자 규칙이 적용된다. 비활성화는 토큰을 revoke하지만 기존 portfolio reference 자동 제거는 규정하지 않는다.
|
||||
- `portfolio-hub/docs/interface-contract.md:301-315`
|
||||
- 포트폴리오 생성·수정 API가 `managed_by_daemon_id`를 입력으로 공개하며 portfolio 소유권과 오류 계약의 기존 경계를 정의한다.
|
||||
|
||||
#### 승인된 결정
|
||||
|
||||
1. 이 이슈는 `portfolio-hub`가 소유하는 repo-local 수정만 다룬다. `trex-web` 코드·테스트와 필수 `strategy_id` UI 적응은 이 이슈의 범위 밖이다.
|
||||
2. 포트폴리오 생성·수정에서 비-null daemon을 지정하려면 지정 시점에 daemon이 존재하고 active이며 포트폴리오 소유자와 같은 사용자가 소유해야 한다.
|
||||
3. admin이 다른 사용자의 포트폴리오를 생성·수정할 때도 daemon 소유자는 호출 admin이 아니라 대상 포트폴리오 소유자와 일치해야 한다.
|
||||
4. 미존재 또는 배정 권한이 없는 foreign daemon은 존재를 숨겨 HTTP 404, 소유 범위 안의 inactive daemon은 HTTP 409, UUID 형식이 아닌 daemon ID는 HTTP 422로 반환한다.
|
||||
5. `PATCH`의 명시적 `managed_by_daemon_id: null` 해제 결함은 별도 이슈로 추적하고 이번 범위에 포함하지 않는다.
|
||||
|
||||
#### Scope
|
||||
|
||||
- `POST /v1/portfolios`가 요청의 비-null `managed_by_daemon_id`를 생성 persistence까지 전달한다.
|
||||
- 생성과 PATCH의 비-null daemon 지정·재지정은 같은 validation 계약을 사용한다.
|
||||
- 요청 daemon ID는 daemon의 server-generated UUID 형식이어야 한다.
|
||||
- daemon은 portfolio persistence 시점에 존재하고 active여야 하며 `daemon.owner_user_id == portfolio.owner_user_id`여야 한다.
|
||||
- admin의 `user_id` override 생성에서는 최종 결정된 target `owner_user_id`를 daemon 소유권 비교 기준으로 사용한다.
|
||||
- 미존재·foreign daemon은 HTTP 404, inactive daemon은 HTTP 409, malformed daemon ID는 HTTP 422로 실패한다.
|
||||
- validation 실패 시 portfolio 생성·수정과 관련 부수 상태가 부분 반영되지 않는다.
|
||||
- daemon validation과 portfolio write는 동시 변경에 대해 하나의 원자적 판정 경계를 가져야 한다. assignment가 먼저 확정된 뒤 daemon이 비활성화되면 기존 deactivation 계약에 따라 service token은 revoke되고 portfolio reference는 보존된다. 비활성화가 먼저 확정되면 새 assignment는 HTTP 409로 실패한다.
|
||||
- 데몬을 선택하지 않은 생성 요청은 `managed_by_daemon_id = null`인 unassigned portfolio를 생성하는 기존 동작을 유지한다.
|
||||
- 성공한 생성 응답과 후속 portfolio 조회는 같은 `managed_by_daemon_id`를 반환한다.
|
||||
- 기존 비-null PATCH 지정·재지정 경로에는 위 validation을 적용하되, 포트폴리오 소유권·disabled 보호·service/client 거부 등 기존 mutation 계약을 유지한다.
|
||||
- `portfolio-hub` 단위/API 테스트와 disposable PostgreSQL 통합 테스트로 성공·미선택·malformed·미존재·foreign·inactive·admin target-owner·동시성·`POST → GET` persistence를 검증한다.
|
||||
|
||||
#### 승인된 수용 기준 후보
|
||||
|
||||
- [ ] 소유자의 active daemon을 선택해 포트폴리오를 생성하면 HTTP 201 응답의 `managed_by_daemon_id`가 선택한 ID와 같다.
|
||||
- [ ] 같은 포트폴리오를 후속 조회해도 선택한 daemon ID가 보존된다.
|
||||
- [ ] daemon 미선택 생성은 `managed_by_daemon_id = null`을 유지한다.
|
||||
- [ ] UUID 형식이 아닌 daemon ID는 HTTP 422이며 portfolio가 생성·수정되지 않는다.
|
||||
- [ ] 미존재 또는 portfolio 소유자와 다른 사용자의 daemon ID는 HTTP 404이며 portfolio가 생성·수정되지 않는다.
|
||||
- [ ] portfolio 소유자 범위의 inactive daemon ID는 HTTP 409이며 portfolio가 생성·수정되지 않는다.
|
||||
- [ ] admin target-user 생성·수정은 target portfolio owner 소유의 active daemon만 허용한다.
|
||||
- [ ] 비-null PATCH 지정·재지정도 생성과 동일한 daemon validation과 오류 계약을 적용한다.
|
||||
- [ ] assignment와 daemon 비활성화가 경합해도 inactive daemon에 새로운 assignment가 확정되는 중간 상태를 허용하지 않는다.
|
||||
- [ ] assignment 확정 후 daemon 비활성화는 기존 portfolio reference를 자동 제거하지 않고 기존 token-revocation 계약을 유지한다.
|
||||
- [ ] 실제 PostgreSQL persistence 경계에서 `POST → GET` 보존과 실패 시 무변경이 검증된다.
|
||||
- [ ] 기존 portfolio 생성·수정의 인증, 소유권, strategy, asset, disabled-state 계약이 회귀하지 않는다.
|
||||
|
||||
#### Out of Scope
|
||||
|
||||
- `trex-web` 코드, 타입, 화면 또는 테스트 변경
|
||||
- `trex-web`의 `/v1/strategies` 조회·선택과 필수 `strategy_id` 계약 적응
|
||||
- `PATCH /v1/portfolios/{id}`의 명시적 `managed_by_daemon_id: null` 해제 지원
|
||||
- 데몬 실행·중지·재시작 또는 배포 자동화
|
||||
- 데몬 credential 관리 정책 변경
|
||||
- 포트폴리오의 자동 데몬 배정
|
||||
- daemon 비활성화 시 기존 portfolio reference 자동 해제 또는 다른 daemon으로 자동 이전
|
||||
- 기존 데이터 전체에 대한 일괄 backfill
|
||||
- 내부 `daemons` 또는 `portfolios` 식별자·테이블 구조 변경
|
||||
- 이 Discovery 승인만으로 구현, main 반영, push, PR, migration 실행, 배포 또는 release 수행
|
||||
|
||||
#### Risks and Dependencies
|
||||
|
||||
- `managed_by_daemon_id`는 DB-level FK가 없는 soft reference이므로 validation과 write를 분리하면 daemon 상태·소유권 변경과의 TOCTOU 위험이 생긴다. persistence transaction과 적절한 row-lock/동등한 직렬화 경계가 필요하다.
|
||||
- daemon 소유자 재할당 기능이 있으므로 portfolio assignment와 동시 실행될 때도 최종 관계가 동일 소유자 규칙을 위반하지 않아야 한다.
|
||||
- `trex-web`의 필수 `strategy_id` 계약 드리프트는 별도 범위다. 이번 backend 동작은 유효한 `strategy_id`를 포함한 직접 API 요청으로 독립 검증한다.
|
||||
- 명시적 null 해제는 알려진 별도 결함으로 남으며 이번 승인 결과를 그 기능의 완료로 해석하지 않는다.
|
||||
|
||||
### Approval State
|
||||
|
||||
- **Status:** APPROVED
|
||||
- **Approved by:** genichin
|
||||
- **Approved at:** 2026-08-15T03:02:35Z
|
||||
- **Approval basis:** OQ-001=A, OQ-002=A, OQ-003=A, OQ-004=A의 단일 선택과 `trex-web` 관련 수정·테스트를 Out of Scope로 두라는 요청자의 명시적 지시
|
||||
- **Unresolved Open Questions:** 없음
|
||||
- **Confirmation:** Discovery 검토·승인 완료
|
||||
- **Authority boundary:** 위 `portfolio-hub` Discovery 범위만 승인하며 구현 완료, lifecycle closure, main 반영, push, PR, 배포 또는 release를 의미하지 않는다.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
### Canonical 경계
|
||||
|
||||
- 위 승인 원문 전체의 `Out of Scope`와 `Authority boundary`를 그대로 적용한다.
|
||||
- 특히 `trex-web` 코드·타입·화면·테스트, `/v1/strategies` UI 적응, PATCH의 명시적 `managed_by_daemon_id: null` 해제, 자동 배정·이전·해제, backfill, 배포·release는 이 Line의 Discovery 범위가 아니다.
|
||||
- 이 단계에서는 REQ·AC를 생성하거나 승인하지 않으며 product source, test, runtime configuration 또는 schema를 변경하지 않는다.
|
||||
|
||||
## Risks and Unknowns
|
||||
|
||||
### Risks
|
||||
|
||||
- `managed_by_daemon_id`가 DB-level FK 없는 soft reference이므로 validation과 write가 분리되면 daemon 비활성화·소유자 변경과 TOCTOU가 발생할 수 있다.
|
||||
- daemon 소유자 재할당과 portfolio assignment가 경합해도 최종 관계가 동일 소유자 규칙을 위반하지 않는 원자적 판정 경계가 필요하다.
|
||||
- `trex-web`의 필수 `strategy_id` 계약 드리프트는 별도 범위이므로 이번 backend 검증은 유효한 `strategy_id`를 포함한 직접 API 요청으로 독립 수행해야 한다.
|
||||
- 명시적 null 해제 결함은 알려진 별도 문제로 남으며 이번 Line 완료로 해석하지 않는다.
|
||||
|
||||
### Assumptions
|
||||
|
||||
- daemon ID의 canonical 형식은 기존 data model의 server-generated UUID다.
|
||||
- daemon 비활성화가 assignment 확정 뒤 발생하면 기존 reference는 보존하고 service token revoke 계약이 적용된다.
|
||||
- 위 가정은 승인된 이슈 본문과 현재 interface/data-model contract에서 직접 유도되며 추가 Open Question은 없다.
|
||||
@@ -0,0 +1,123 @@
|
||||
# GitHub Issue #27 — 승인된 Discovery 원문 증거
|
||||
|
||||
- Source: https://github.com/fourblocks2020/portfolio-hub/issues/27
|
||||
- Repository: `fourblocks2020/portfolio-hub`
|
||||
- Issue: `#27`
|
||||
- Title: `trex-web : 포트폴리오 추가시 연결 데몬을 설정하였음에도 지정되지 않음`
|
||||
- State: `open`
|
||||
- Created at: `2026-07-21T14:42:15Z`
|
||||
- Updated at: `2026-08-15T03:03:53Z`
|
||||
- Body SHA-256: `17668fbfac2419713e8247637fbf05631d93a71bdb74db0fe122743fba8d44e4`
|
||||
- Captured for: `line-0004` / `dcy-0004`
|
||||
- Evidence class: 외부 승인 원문의 immutable-by-hash snapshot; canonical lifecycle state는 `dcy-0004.md` frontmatter가 소유한다.
|
||||
|
||||
## Exact approved issue body
|
||||
|
||||
## 원래 제보
|
||||
|
||||
> 편집으로 다시 지정해야 반영 됨.
|
||||
|
||||
## ProofLine Discovery
|
||||
|
||||
- **Status:** APPROVED
|
||||
- **Source:** 프로젝트 보드 `trex` / 현재 `fourblocks2020/portfolio-hub#27` (최초 `fourblocks2020/trex-web#10`에서 이전)
|
||||
- **검토 기준:** `portfolio-hub` `2a1a637fb9fb19944991cb902530faf72f0e9f84`, 증상 경계 확인용 `trex-web` `b72ccd7cd639447e71d7e785857d2c503e3a1841`
|
||||
- **승인 경계:** 이 승인은 아래 `portfolio-hub` Discovery 범위를 승인한다. 구현 완료, `trex-web` 변경, main 반영, push, PR, 배포 또는 release 승인을 의미하지 않는다.
|
||||
|
||||
### Problem
|
||||
|
||||
포트폴리오 추가 화면에서 사용자가 `연결 데몬`을 선택해 생성해도 생성된 포트폴리오의 `managed_by_daemon_id`가 저장되지 않는다. 같은 포트폴리오를 편집하여 비-null 데몬을 다시 지정하면 반영된다.
|
||||
|
||||
프런트엔드는 생성 요청에 선택값을 포함하지만, `portfolio-hub`의 `POST /v1/portfolios` 처리기가 해당 값을 저장소 생성 호출로 전달하지 않는다. 반면 `PATCH /v1/portfolios/{portfolio_id}`는 비-null `managed_by_daemon_id`를 update fields에 포함하므로 “생성에서는 누락되고 편집에서는 반영”되는 제보와 코드 경로가 일치한다.
|
||||
|
||||
### Evidence
|
||||
|
||||
- Live issue: https://github.com/fourblocks2020/portfolio-hub/issues/27
|
||||
- 원래 제보: “편집으로 다시 지정해야 반영 됨.”
|
||||
- 최초 작성: `2026-07-21T14:42:15Z`
|
||||
- `trex-web/features/portfolio/portfolio-create-form.tsx:83-91`
|
||||
- 선택한 데몬을 `managed_by_daemon_id: selectedDaemonId || null`로 생성 mutation에 전달한다.
|
||||
- `trex-web/lib/api/portfolio-hub.ts:87-95,251-256`
|
||||
- 생성 요청 타입과 `POST /v1/portfolios` wrapper가 `managed_by_daemon_id`를 허용하고 payload를 그대로 전송한다.
|
||||
- `portfolio-hub/app/schemas/v1.py:235-258`
|
||||
- 생성·수정 schema 모두 `managed_by_daemon_id` 입력을 허용한다.
|
||||
- `portfolio-hub/app/api/v1_portfolios.py:170-183`
|
||||
- 생성 handler가 `repo.create_portfolio(...)`에 `managed_by_daemon_id`를 전달하지 않는다.
|
||||
- `portfolio-hub/app/repositories/portfolio_repository.py:150-165`
|
||||
- 현재 생성 port 시그니처에도 `managed_by_daemon_id`가 없다.
|
||||
- `portfolio-hub/app/api/v1_portfolios.py:222-235`
|
||||
- 편집 handler는 `exclude_none=True`로 얻은 비-null fields를 `repo.update_portfolio(...)`에 전달하므로 비-null 데몬 재지정은 저장된다.
|
||||
- `portfolio-hub/app/api/v1_daemons.py:26-56`
|
||||
- 일반 daemon 목록은 호출자 소유 범위이며 active와 inactive를 `is_active`로 구분해 반환한다.
|
||||
- `portfolio-hub/docs/data-model.md:231-259`
|
||||
- daemon ID는 server-generated UUID이고, daemon은 사용자 소유이며, service token 바인딩에는 존재·active·동일 소유자 규칙이 적용된다. 비활성화는 토큰을 revoke하지만 기존 portfolio reference 자동 제거는 규정하지 않는다.
|
||||
- `portfolio-hub/docs/interface-contract.md:301-315`
|
||||
- 포트폴리오 생성·수정 API가 `managed_by_daemon_id`를 입력으로 공개하며 portfolio 소유권과 오류 계약의 기존 경계를 정의한다.
|
||||
|
||||
### 승인된 결정
|
||||
|
||||
1. 이 이슈는 `portfolio-hub`가 소유하는 repo-local 수정만 다룬다. `trex-web` 코드·테스트와 필수 `strategy_id` UI 적응은 이 이슈의 범위 밖이다.
|
||||
2. 포트폴리오 생성·수정에서 비-null daemon을 지정하려면 지정 시점에 daemon이 존재하고 active이며 포트폴리오 소유자와 같은 사용자가 소유해야 한다.
|
||||
3. admin이 다른 사용자의 포트폴리오를 생성·수정할 때도 daemon 소유자는 호출 admin이 아니라 대상 포트폴리오 소유자와 일치해야 한다.
|
||||
4. 미존재 또는 배정 권한이 없는 foreign daemon은 존재를 숨겨 HTTP 404, 소유 범위 안의 inactive daemon은 HTTP 409, UUID 형식이 아닌 daemon ID는 HTTP 422로 반환한다.
|
||||
5. `PATCH`의 명시적 `managed_by_daemon_id: null` 해제 결함은 별도 이슈로 추적하고 이번 범위에 포함하지 않는다.
|
||||
|
||||
### Scope
|
||||
|
||||
- `POST /v1/portfolios`가 요청의 비-null `managed_by_daemon_id`를 생성 persistence까지 전달한다.
|
||||
- 생성과 PATCH의 비-null daemon 지정·재지정은 같은 validation 계약을 사용한다.
|
||||
- 요청 daemon ID는 daemon의 server-generated UUID 형식이어야 한다.
|
||||
- daemon은 portfolio persistence 시점에 존재하고 active여야 하며 `daemon.owner_user_id == portfolio.owner_user_id`여야 한다.
|
||||
- admin의 `user_id` override 생성에서는 최종 결정된 target `owner_user_id`를 daemon 소유권 비교 기준으로 사용한다.
|
||||
- 미존재·foreign daemon은 HTTP 404, inactive daemon은 HTTP 409, malformed daemon ID는 HTTP 422로 실패한다.
|
||||
- validation 실패 시 portfolio 생성·수정과 관련 부수 상태가 부분 반영되지 않는다.
|
||||
- daemon validation과 portfolio write는 동시 변경에 대해 하나의 원자적 판정 경계를 가져야 한다. assignment가 먼저 확정된 뒤 daemon이 비활성화되면 기존 deactivation 계약에 따라 service token은 revoke되고 portfolio reference는 보존된다. 비활성화가 먼저 확정되면 새 assignment는 HTTP 409로 실패한다.
|
||||
- 데몬을 선택하지 않은 생성 요청은 `managed_by_daemon_id = null`인 unassigned portfolio를 생성하는 기존 동작을 유지한다.
|
||||
- 성공한 생성 응답과 후속 portfolio 조회는 같은 `managed_by_daemon_id`를 반환한다.
|
||||
- 기존 비-null PATCH 지정·재지정 경로에는 위 validation을 적용하되, 포트폴리오 소유권·disabled 보호·service/client 거부 등 기존 mutation 계약을 유지한다.
|
||||
- `portfolio-hub` 단위/API 테스트와 disposable PostgreSQL 통합 테스트로 성공·미선택·malformed·미존재·foreign·inactive·admin target-owner·동시성·`POST → GET` persistence를 검증한다.
|
||||
|
||||
### 승인된 수용 기준 후보
|
||||
|
||||
- [ ] 소유자의 active daemon을 선택해 포트폴리오를 생성하면 HTTP 201 응답의 `managed_by_daemon_id`가 선택한 ID와 같다.
|
||||
- [ ] 같은 포트폴리오를 후속 조회해도 선택한 daemon ID가 보존된다.
|
||||
- [ ] daemon 미선택 생성은 `managed_by_daemon_id = null`을 유지한다.
|
||||
- [ ] UUID 형식이 아닌 daemon ID는 HTTP 422이며 portfolio가 생성·수정되지 않는다.
|
||||
- [ ] 미존재 또는 portfolio 소유자와 다른 사용자의 daemon ID는 HTTP 404이며 portfolio가 생성·수정되지 않는다.
|
||||
- [ ] portfolio 소유자 범위의 inactive daemon ID는 HTTP 409이며 portfolio가 생성·수정되지 않는다.
|
||||
- [ ] admin target-user 생성·수정은 target portfolio owner 소유의 active daemon만 허용한다.
|
||||
- [ ] 비-null PATCH 지정·재지정도 생성과 동일한 daemon validation과 오류 계약을 적용한다.
|
||||
- [ ] assignment와 daemon 비활성화가 경합해도 inactive daemon에 새로운 assignment가 확정되는 중간 상태를 허용하지 않는다.
|
||||
- [ ] assignment 확정 후 daemon 비활성화는 기존 portfolio reference를 자동 제거하지 않고 기존 token-revocation 계약을 유지한다.
|
||||
- [ ] 실제 PostgreSQL persistence 경계에서 `POST → GET` 보존과 실패 시 무변경이 검증된다.
|
||||
- [ ] 기존 portfolio 생성·수정의 인증, 소유권, strategy, asset, disabled-state 계약이 회귀하지 않는다.
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- `trex-web` 코드, 타입, 화면 또는 테스트 변경
|
||||
- `trex-web`의 `/v1/strategies` 조회·선택과 필수 `strategy_id` 계약 적응
|
||||
- `PATCH /v1/portfolios/{id}`의 명시적 `managed_by_daemon_id: null` 해제 지원
|
||||
- 데몬 실행·중지·재시작 또는 배포 자동화
|
||||
- 데몬 credential 관리 정책 변경
|
||||
- 포트폴리오의 자동 데몬 배정
|
||||
- daemon 비활성화 시 기존 portfolio reference 자동 해제 또는 다른 daemon으로 자동 이전
|
||||
- 기존 데이터 전체에 대한 일괄 backfill
|
||||
- 내부 `daemons` 또는 `portfolios` 식별자·테이블 구조 변경
|
||||
- 이 Discovery 승인만으로 구현, main 반영, push, PR, migration 실행, 배포 또는 release 수행
|
||||
|
||||
### Risks and Dependencies
|
||||
|
||||
- `managed_by_daemon_id`는 DB-level FK가 없는 soft reference이므로 validation과 write를 분리하면 daemon 상태·소유권 변경과의 TOCTOU 위험이 생긴다. persistence transaction과 적절한 row-lock/동등한 직렬화 경계가 필요하다.
|
||||
- daemon 소유자 재할당 기능이 있으므로 portfolio assignment와 동시 실행될 때도 최종 관계가 동일 소유자 규칙을 위반하지 않아야 한다.
|
||||
- `trex-web`의 필수 `strategy_id` 계약 드리프트는 별도 범위다. 이번 backend 동작은 유효한 `strategy_id`를 포함한 직접 API 요청으로 독립 검증한다.
|
||||
- 명시적 null 해제는 알려진 별도 결함으로 남으며 이번 승인 결과를 그 기능의 완료로 해석하지 않는다.
|
||||
|
||||
## Approval State
|
||||
|
||||
- **Status:** APPROVED
|
||||
- **Approved by:** genichin
|
||||
- **Approved at:** 2026-08-15T03:02:35Z
|
||||
- **Approval basis:** OQ-001=A, OQ-002=A, OQ-003=A, OQ-004=A의 단일 선택과 `trex-web` 관련 수정·테스트를 Out of Scope로 두라는 요청자의 명시적 지시
|
||||
- **Unresolved Open Questions:** 없음
|
||||
- **Confirmation:** Discovery 검토·승인 완료
|
||||
- **Authority boundary:** 위 `portfolio-hub` Discovery 범위만 승인하며 구현 완료, lifecycle closure, main 반영, push, PR, 배포 또는 release를 의미하지 않는다.
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
id: "line-0004"
|
||||
---
|
||||
Reference in New Issue
Block a user