POST /v1/portfolios now forwards a non-null managed_by_daemon_id into create persistence, and both create and PATCH apply the same daemon validation contract (UUID format -> 422, missing/foreign -> 404, inactive -> 409, admin target-owner basis). Daemon validation locks the daemon row FOR SHARE inside the portfolio write transaction so validation and write form one atomic decision boundary. Adds domain errors, repository protocol/signature updates, API/PostgreSQL tests (POST->GET persistence, no partial state on failure, assignment vs deactivation race), and contract/data-model documentation.
5.3 KiB
5.3 KiB
구현 진행 상황
- 대상: 포트폴리오 추가 시 연결 데몬을 설정해도 지정되지 않음 (GitHub Issue #27)
- 브랜치:
line/line-0004-implementation - 기준:
main(bb3bb5d3f0ce57358f7385084fba1343f7ff06e1) - 상태: 로컬 구현·검증 완료 (미커밋)
- 최종 업데이트: 2026-08-15 15:20 KST
구현 계획 및 작업 목록
- 공유 daemon validation 도메인 오류 정의
DaemonNotFoundError(HTTP 404)·DaemonInactiveError(HTTP 409)를app/domain/errors.py에 추가해 전 계층에서 같은 오류 계약을 사용한다.
create_portfolio저장소 계약·구현에managed_by_daemon_id전달- 프로토콜 시그니처(
portfolio_repository.py)와 PostgreSQL 구현(postgres.py)에managed_by_daemon_id파라미터를 추가하고 INSERT 컬럼까지 반영해 생성 persistence 경계에 누락을 제거한다.
- 프로토콜 시그니처(
- 생성·수정 트랜잭션 안의 원자적 daemon 검증 경계
- 기존
_lock_active_strategy패턴처럼 daemon row를FOR SHARElock하고 존재·소유권·active를 같은 트랜잭션에서 검증해, portfolio write와 하나의 원자적 판정으로 묶어 TOCTOU를 차단한다.
- 기존
POST /v1/portfolios생성 handler에서 비-null daemon 전달·오류 매핑- UUID 형식이 아니면 422, 미존재·foreign(타인 소유)이면 404, inactive면 409로 매핑하고 admin의
user_idoverride 생성은 최종 target owner 기준으로 검증한다.
- UUID 형식이 아니면 422, 미존재·foreign(타인 소유)이면 404, inactive면 409로 매핑하고 admin의
PATCH /v1/portfolios/{portfolio_id}비-null 지정·재지정에도 동일 검증 적용- 기존 비-null PATCH 지정·재지정 경로에 생성과 동일한 daemon 검증·오류 계약을 적용하되, 포트폴리오 소유권·disabled 보호·service/client 거부 등 기존 mutation 계약은 유지한다.
- 단위/API 테스트 추가
- 성공 생성 응답의
managed_by_daemon_id일치, 미선택 생성 시 null 유지, malformed 422, 미존재·foreign 404, inactive 409, admin target-owner 허용/거부, PATCH 지정·재지정을 검증한다.
- 성공 생성 응답의
- 실제 PostgreSQL 통합 테스트 추가
POST → GETpersistence 보존, 검증 실패 시 portfolio 무변경, assignment와 daemon 비활성화가 경합해도 inactive daemon에 새 assignment가 확정되지 않는 동시성 race를 검증한다.
- 문서 갱신
docs/interface-contract.md,docs/data-model.md에 daemon 배정 검증 계약·오류 코드·원자적 판정 경계를 반영한다.
- 회귀·정적 검사 및 진행 상황 업데이트
- 기존 portfolio 생성·수정의 인증·소유권·strategy·asset·disabled 계약 회귀, 전체 관련 테스트, compile·
git diff --check·Ruff 검사를 실행하고 진행 상황 파일을 갱신한다.
- 기존 portfolio 생성·수정의 인증·소유권·strategy·asset·disabled 계약 회귀, 전체 관련 테스트, compile·
진행 기록
- 2026-08-15 13:00 KST — Issue #27의 승인된 Discovery(
dcy-0004) 범위를 기준으로 구현 계획을 확정했다. 근본 원인(v1_portfolios.py생성 handler가repo.create_portfolio(...)에managed_by_daemon_id를 전달하지 않음)과 승인된 결정(존재·active·동일 소유자, 404/409/422 오류 계약, 원자적 판정 경계)을 반영해 작업 목록을 구성했다. - 2026-08-15 15:10 KST —
app/domain/errors.py에DaemonNotFoundError·DaemonInactiveError를 추가하고,create_portfolio프로토콜·구현과 INSERT 컬럼에managed_by_daemon_id를 반영했다.postgres.py에_lock_active_daemon(daemon rowFOR SHARE+ 존재·동일 소유자·active 검증)을 추가해 생성·수정 트랜잭션 안에서 원자적 판정 경계를 구성했다. - 2026-08-15 15:10 KST —
POST /v1/portfolios와PATCH /v1/portfolios/{id}handler에 UUID 형식(422)·미존재/foreign(404)·inactive(409) 검증과 오류 매핑을 적용했다. admin target-user 생성은 최종 target owner 기준으로 daemon 소유권을 검증한다. - 2026-08-15 15:12 KST — 기존
test_v1_portfolios_crud.py의_FakeRepo.create_portfolio시그니처를 새 파라미터에 맞춰 보완했다. - 2026-08-15 15:15 KST — 전용 API 테스트
tests/test_v1_portfolios_daemon_assignment.py(13개)를 추가했다. POST 성공·null 유지·malformed 422·미존재/foreign 404·inactive 409·admin target-owner 허용/거부, PATCH 지정·재지정·malformed·foreign·inactive를 검증해 통과했다. - 2026-08-15 15:18 KST — 실제 PostgreSQL 통합 테스트
tests/test_v1_portfolios_daemon_postgres_integration.py(4개)를 추가했다. HTTPPOST → GETpersistence, 검증 실패 시 portfolio 무변경, assignment vs deactivation race의 허용 outcome, PATCH 재배정 persistence를 실제 DB에서 검증했다. - 2026-08-15 15:20 KST — 회귀 검증: 전체 단위 스위트
625 passed, 11 skipped, 실제 PostgreSQL 통합(daemon 4 + strategy 4)8 passed. Python compile,git diff --check, 변경 핵심 파일 Ruff 검사 통과(Ruff 이슈 44건은 기준 커밋과 동일한 기존 항목이며 신규 도입 없음).
각 작업이 실제로 완료되고 검증될 때마다 이 댓글의 체크박스와 진행 기록을 갱신한다. 체크 표시는 구현 완료 또는 해당 검증의 실제 통과를 의미하며, 배포·릴리스 완료를 의미하지 않는다. 현재 변경은 기준 HEAD 위 미커밋 상태로 보존되어 있으며 커밋·push·PR·main 반영·배포는 수행하지 않았다.