Files
portfolio-hub/.proofline/lines/line-0004/evidence/implementation-fix-rerequest.md
T

7.5 KiB

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 소유자를 변경하면 다음 불변식이 깨진다.

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는 참조 충돌로 실패한다.

금지되는 최종 상태:

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를 적용한다.

export TIMESCALEDB_HOST=127.0.0.1
export TIMESCALEDB_PORT=5432
export TIMESCALEDB_NAME=<disposable-db>
export TIMESCALEDB_USER=portfolio_hub_user
export TIMESCALEDB_PASSWORD=<local-test-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. 최소 재검증

.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
.venv/bin/python -m pytest -q
PORTFOLIOHUB_RUN_POSTGRES_INTEGRATION=1 \
  .venv/bin/python -m pytest -q \
  tests/test_v1_portfolios_daemon_postgres_integration.py
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
.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 완료를 의미하지 않는다.