# line-0004 구현 에이전트 수정 재요청 - **대상 이슈:** `fourblocks2020/portfolio-hub#27` - **대상 Line:** `line-0004` - **작업 worktree:** `/home/genichin/workspace/trex-lead/repos/portfolio-hub/.worktrees/line-0004` - **작업 브랜치:** `line/line-0004-implementation` - **수정 기준 커밋:** `e2102b6f2b855b7679ab67818202ef706d9848fb` - **범위 권위:** `.proofline/lines/line-0004/dcy-0004.md` - **1차 QC 결과:** `.proofline/lines/line-0004/evidence/first-qc-review.md` - **요청 상태:** QC 반려 수정 요청 ## 1. 작업 원칙 기존 구현의 의미와 승인된 Discovery 범위를 변경하지 말고 아래 blocker 및 관련 회귀 검증만 수정한다. 새로운 제품 결정, 다른 서비스 계약 영향, 승인 범위와의 충돌이 발견되면 구현을 중단하고 팀장에게 반환한다. `trex-web`은 명시적인 Out of Scope다. 해당 저장소의 코드·타입·화면·테스트를 변경하거나 실행하지 않는다. ## 2. 필수 수정 — 참조 중 daemon owner transfer 차단 현재 daemon이 portfolio에 배정된 뒤 `update_daemon_owner()`로 daemon 소유자를 변경하면 다음 불변식이 깨진다. ```text daemon.owner_user_id == portfolio.owner_user_id ``` `update_daemon_owner()`에서 해당 daemon을 참조하는 portfolio가 하나라도 있으면 owner transfer가 원자적으로 실패하도록 수정한다. 필수 결과: - 참조 중 daemon owner transfer: `409 Conflict` - daemon owner: 변경되지 않음 - portfolio `managed_by_daemon_id`: 변경되지 않음 - 기존 broker credentials: 삭제되지 않음 - transaction 일부만 반영되는 상태: 금지 다음 자동 동작은 승인 범위 밖이므로 구현하지 않는다. - portfolio assignment 자동 해제 - portfolio를 새 daemon owner에게 자동 이전 - 다른 daemon으로 자동 재배정 - 기존 데이터 backfill ## 3. 필수 수정 — assignment와 owner transfer 직렬화 portfolio assignment와 daemon owner transfer가 동일 daemon을 대상으로 동시에 실행될 때 일관된 row-lock 순서와 transaction 경계를 사용한다. 허용되는 결과: 1. owner transfer가 먼저 commit되면 이전 owner portfolio의 assignment는 foreign daemon으로 판단되어 실패한다. 2. assignment가 먼저 commit되면 owner transfer는 참조 충돌로 실패한다. 금지되는 최종 상태: ```text portfolio.managed_by_daemon_id = daemon.id portfolio.owner_user_id != daemon.owner_user_id ``` 동시 실행이 timeout 또는 deadlock으로 끝나지 않도록 lock order를 검토한다. ## 4. 도메인 및 API 오류 계약 참조 중 owner transfer 충돌을 daemon 미존재 오류와 구분한다. 권장 구현 경계: - 전용 domain error 추가 - repository `update_daemon_owner()`에서 전용 error 발생 - `PATCH /v1/admin/daemons/{daemon_id}`에서 HTTP 409로 매핑 - 기존 daemon 미존재 404 유지 - 기존 admin 인증·권한 계약 유지 - 미참조 owner transfer의 기존 credential invalidation 정책 유지 ## 5. 필수 테스트 ### 5.1 단위/API 다음을 추가하거나 보강한다. - [ ] 미참조 daemon owner transfer 성공 - [ ] 참조 중 daemon owner transfer `409` - [ ] `409` 후 daemon owner 무변경 - [ ] `409` 후 portfolio assignment 유지 - [ ] `409` 후 broker credentials 유지 - [ ] daemon 미존재 `404` 회귀 없음 - [ ] PATCH missing daemon `404` 및 portfolio 무변경 - [ ] admin PATCH에서 호출자 대신 target portfolio owner 기준 검증 - [ ] deactivation 후 기존 assignment reference 보존 - [ ] deactivation 시 기존 service token revoke 유지 ### 5.2 실제 PostgreSQL migration head가 적용된 disposable PostgreSQL에서 검증한다. #### 순차 owner transfer 1. owner A daemon 생성 2. owner A portfolio에 daemon 배정 3. daemon을 owner B로 이전 시도 4. owner transfer 실패 5. 동일 소유자 불변식 유지 6. owner·assignment·credential 무변경 확인 #### assignment/owner-transfer race 1. 별도 connection/thread에서 assignment와 owner transfer 동시 실행 2. 두 실행 순서를 모두 유도하거나 반복 검증 3. 허용된 결과만 발생하는지 확인 4. 최종 DB 동일 소유자 불변식 확인 5. thread timeout/deadlock 없음 확인 #### 미참조 owner transfer 1. portfolio가 참조하지 않는 daemon owner transfer 성공 2. 기존 broker credential invalidation 정책 유지 ## 6. PostgreSQL 실행 전제 완전히 빈 DB에는 먼저 migration head를 적용한다. ```bash export TIMESCALEDB_HOST=127.0.0.1 export TIMESCALEDB_PORT=5432 export TIMESCALEDB_NAME= export TIMESCALEDB_USER=portfolio_hub_user export TIMESCALEDB_PASSWORD= export TIMESCALEDB_SSLMODE=prefer export PFHUB_DB_ENABLED=true .venv/bin/pfhub migrate-db PORTFOLIOHUB_RUN_POSTGRES_INTEGRATION=1 \ .venv/bin/python -m pytest -q \ tests/test_v1_portfolios_daemon_postgres_integration.py ``` 실행 후 disposable DB를 삭제한다. 기존 공유·운영 DB를 오염시키지 않는다. ## 7. 문서 및 evidence 실제 동작과 일치하도록 필요한 범위에서 갱신한다. - `docs/interface-contract.md` - `docs/data-model.md` - daemon admin owner-transfer runtime flow 문서 - `.proofline/lines/line-0004/evidence/implementation-progress.md` 문서에 다음을 명시한다. - 참조 중 daemon owner transfer는 409 - owner transfer와 assignment의 원자적 직렬화 - 자동 assignment 해제·portfolio owner 변경 없음 - 충돌 시 owner·assignment·credential partial mutation 없음 `implementation-progress.md`의 “미커밋” 표기를 실제 상태로 수정하고 다음 실행 근거를 기록한다. - QC 반려 blocker와 해결 방식 - 수정 파일 목록 - 수정 커밋 - 집중 테스트 결과 - 전체 회귀 결과 - 실제 PostgreSQL 순차·경합 검증 결과 - Ruff, compileall, diff check, ProofLine validate 결과 - push·PR·main 반영·배포·release 미수행 경계 QC 재검증 전에는 implementation complete 또는 lifecycle 완료 문서를 새로 만들지 않는다. ## 8. 최소 재검증 ```bash .venv/bin/python -m pytest -q \ tests/test_v1_portfolios_daemon_assignment.py \ tests/test_v1_portfolios_crud.py \ tests/test_bat022_req053_admin_daemon_crud.py ``` ```bash .venv/bin/python -m pytest -q ``` ```bash PORTFOLIOHUB_RUN_POSTGRES_INTEGRATION=1 \ .venv/bin/python -m pytest -q \ tests/test_v1_portfolios_daemon_postgres_integration.py ``` ```bash ruff check \ app/api/v1_portfolios.py \ app/api/v1_admin_daemons.py \ app/domain/errors.py \ app/repositories/portfolio_repository.py \ app/repositories/postgres.py \ tests/test_v1_portfolios_daemon_assignment.py \ tests/test_v1_portfolios_daemon_postgres_integration.py \ tests/test_bat022_req053_admin_daemon_crud.py ``` ```bash .venv/bin/python -m compileall -q app tests git diff --check proofline validate ``` ## 9. 금지 사항 - `trex-web` 변경·테스트 금지 - approved Discovery 범위 변경 금지 - 명시적 `managed_by_daemon_id: null` 해제 구현 금지 - 자동 daemon 해제·이전·재배정·backfill 금지 - main 작업 금지 - push·PR·배포·release 금지 ## 10. 반환 조건 수정·테스트·로컬 커밋 후 다음을 팀장에게 반환한다. 1. 새 커밋 SHA 2. 변경 파일 목록 3. blocker 해결 방식과 transaction/lock order 4. 테스트별 실제 pass/fail/skip 수 5. 실제 PostgreSQL 순차·경합 결과 6. 남은 비차단 위험 7. worktree clean 여부 수정 완료는 QC 재검증 입력일 뿐 QC PASS나 lifecycle 완료를 의미하지 않는다.