From 19b63a336e1582b9db97dfb835aae1ffd56871dd Mon Sep 17 00:00:00 2001 From: Justin Ko Date: Sat, 15 Aug 2026 13:59:21 +0900 Subject: [PATCH] docs(proofline): approve line 0004 requirements --- .proofline/criteria/ac-0024.md | 17 +++++++ .proofline/criteria/ac-0025.md | 17 +++++++ .proofline/criteria/ac-0026.md | 17 +++++++ .proofline/criteria/ac-0027.md | 17 +++++++ .proofline/criteria/ac-0028.md | 17 +++++++ .proofline/criteria/ac-0029.md | 17 +++++++ .proofline/criteria/ac-0030.md | 17 +++++++ .proofline/criteria/ac-0031.md | 17 +++++++ .proofline/identities.json | 2 +- .proofline/lines/line-0004/req-0004.md | 61 ++++++++++++++++++++++++++ 10 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 .proofline/criteria/ac-0024.md create mode 100644 .proofline/criteria/ac-0025.md create mode 100644 .proofline/criteria/ac-0026.md create mode 100644 .proofline/criteria/ac-0027.md create mode 100644 .proofline/criteria/ac-0028.md create mode 100644 .proofline/criteria/ac-0029.md create mode 100644 .proofline/criteria/ac-0030.md create mode 100644 .proofline/criteria/ac-0031.md create mode 100644 .proofline/lines/line-0004/req-0004.md diff --git a/.proofline/criteria/ac-0024.md b/.proofline/criteria/ac-0024.md new file mode 100644 index 0000000..014bf9a --- /dev/null +++ b/.proofline/criteria/ac-0024.md @@ -0,0 +1,17 @@ +--- +id: "ac-0024" +status: active +--- + +# 포트폴리오 생성 시 데몬 배정 저장 및 조회 보존 + +## Criterion + +유효한 `strategy_id`와 portfolio owner가 소유한 active daemon의 server-generated UUID를 포함해 포트폴리오를 생성하면 HTTP 201 응답과 후속 portfolio 조회의 `managed_by_daemon_id`가 요청한 daemon ID와 같아야 한다. + +## Verification + +- 성공 조건: POST 응답과 저장소 조회 및 API 후속 조회가 모두 같은 non-null daemon ID를 반환한다. +- 실패·경계 조건: persistence 경계에서 daemon ID가 누락되거나 `null`로 바뀌면 실패한다. +- 검사 방법: API test와 migration head가 적용된 disposable PostgreSQL에서 `POST → GET` read-back을 실행한다. +- 현재 구현 평가: `e2102b6`의 생성 handler·repository INSERT 경로와 PostgreSQL integration test가 대응하며 2차 QC에서 관련 suite가 통과했다. diff --git a/.proofline/criteria/ac-0025.md b/.proofline/criteria/ac-0025.md new file mode 100644 index 0000000..d341a16 --- /dev/null +++ b/.proofline/criteria/ac-0025.md @@ -0,0 +1,17 @@ +--- +id: "ac-0025" +status: active +--- + +# 데몬 미선택 생성의 미배정 상태 유지 + +## Criterion + +`managed_by_daemon_id`를 생략하거나 선택하지 않은 유효한 포트폴리오 생성은 기존 동작대로 `managed_by_daemon_id = null`인 미배정 포트폴리오를 생성해야 한다. + +## Verification + +- 성공 조건: POST 응답과 후속 조회에서 `managed_by_daemon_id`가 `null`이다. +- 실패·경계 조건: 서버가 daemon을 자동 선택·배정하거나 요청하지 않은 reference를 생성하면 실패한다. +- 검사 방법: 데몬 필드가 없거나 null인 생성 API test와 persistence read-back을 실행한다. +- 현재 구현 평가: 미선택 생성 전용 API test가 존재하고 2차 QC 집중 suite에서 통과했다. diff --git a/.proofline/criteria/ac-0026.md b/.proofline/criteria/ac-0026.md new file mode 100644 index 0000000..776abb3 --- /dev/null +++ b/.proofline/criteria/ac-0026.md @@ -0,0 +1,17 @@ +--- +id: "ac-0026" +status: active +--- + +# 포트폴리오 소유자 기준 데몬 배정 자격 검증 + +## Criterion + +생성 또는 PATCH로 비-null daemon을 지정할 때 daemon은 지정 시점에 존재하고 active이며 `daemon.owner_user_id == portfolio.owner_user_id`여야 하고, admin 대행 요청도 호출 admin이 아니라 최종 target portfolio owner를 비교 기준으로 사용해야 한다. + +## Verification + +- 성공 조건: 일반 owner와 admin 대행 요청에서 target portfolio owner가 소유한 active daemon만 배정된다. +- 실패·경계 조건: 호출 admin 소유이지만 target owner와 다른 daemon, foreign daemon 또는 inactive daemon은 배정되지 않는다. +- 검사 방법: 일반 owner 및 admin target-owner 허용·거부 API test와 저장소 소유자 비교 경로를 검사한다. +- 현재 구현 평가: 생성과 PATCH가 repository의 동일 owner 기준 validation을 사용하고 admin target-owner 테스트가 2차 QC 집중 suite에 포함됐다. diff --git a/.proofline/criteria/ac-0027.md b/.proofline/criteria/ac-0027.md new file mode 100644 index 0000000..ec6aa67 --- /dev/null +++ b/.proofline/criteria/ac-0027.md @@ -0,0 +1,17 @@ +--- +id: "ac-0027" +status: active +--- + +# 잘못된 데몬 배정 요청의 오류 계약과 무변경 보장 + +## Criterion + +비-null daemon 지정에서 UUID 형식이 잘못되면 HTTP 422, 미존재 또는 target portfolio owner와 다른 foreign daemon이면 HTTP 404, target owner 범위의 inactive daemon이면 HTTP 409를 반환하고 portfolio 생성·수정 및 관련 상태를 부분 반영하지 않아야 한다. + +## Verification + +- 성공 조건: malformed·missing·foreign·inactive 입력이 각각 승인된 상태 코드로 실패하고 기존 portfolio snapshot이 유지된다. +- 실패·경계 조건: foreign daemon 존재가 다른 상태 코드나 응답으로 노출되거나 실패한 create/update가 일부 저장되면 실패한다. +- 검사 방법: POST/PATCH negative API test와 실제 PostgreSQL 실패 전후 row 비교를 실행한다. +- 현재 구현 평가: 전용 domain error와 API mapping 및 failure-no-mutation tests가 존재하며 2차 QC에서 통과했다. diff --git a/.proofline/criteria/ac-0028.md b/.proofline/criteria/ac-0028.md new file mode 100644 index 0000000..383c28c --- /dev/null +++ b/.proofline/criteria/ac-0028.md @@ -0,0 +1,17 @@ +--- +id: "ac-0028" +status: active +--- + +# 포트폴리오 수정 시 데몬 지정 및 재지정 계약 + +## Criterion + +기존 포트폴리오의 PATCH에서 non-null `managed_by_daemon_id`를 처음 지정하거나 다른 daemon으로 재지정하면 생성과 같은 UUID·존재·active·target-owner 검증과 오류 계약을 적용하고 성공 시 선택한 daemon ID를 저장·반환해야 한다. + +## Verification + +- 성공 조건: 유효한 최초 지정과 재지정이 persistence되고 응답·후속 조회에 반영된다. +- 실패·경계 조건: malformed·missing·foreign·inactive daemon 요청은 승인된 상태 코드로 실패하고 기존 assignment를 변경하지 않으며, 명시적 null 해제는 이 AC의 성공 조건으로 간주하지 않는다. +- 검사 방법: PATCH 지정·재지정 및 negative API test와 실제 PostgreSQL persistence test를 실행한다. +- 현재 구현 평가: PATCH validation·repository update 경로와 재지정 integration test가 존재하며 2차 QC에서 통과했다. diff --git a/.proofline/criteria/ac-0029.md b/.proofline/criteria/ac-0029.md new file mode 100644 index 0000000..dcce890 --- /dev/null +++ b/.proofline/criteria/ac-0029.md @@ -0,0 +1,17 @@ +--- +id: "ac-0029" +status: active +--- + +# 데몬 비활성화와 배정의 원자적 직렬화 + +## Criterion + +portfolio assignment와 daemon 비활성화가 경합할 때 검증과 write를 직렬화하여 비활성화가 먼저 확정된 daemon에는 새 assignment를 저장하지 않아야 하며, assignment가 먼저 확정된 뒤 비활성화되면 기존 portfolio reference를 보존하고 기존 service-token revoke 계약을 적용해야 한다. + +## Verification + +- 성공 조건: 두 commit 순서 모두 허용된 최종 상태만 만들고 assignment-first 이후 deactivation은 reference 보존과 token revoke를 함께 관찰한다. +- 실패·경계 조건: inactive daemon에 새 assignment가 commit되거나 deactivation이 기존 reference를 자동 제거하거나 bound active token을 유지하면 실패한다. +- 검사 방법: 별도 connection/thread를 사용하는 disposable PostgreSQL race test와 deactivation/reference/token 결합 test를 실행하고 timeout·deadlock을 검사한다. +- 현재 구현 평가: daemon `FOR SHARE` validation과 deactivation UPDATE 직렬화 및 실제 PostgreSQL tests가 존재하며 2차 QC integration에서 통과했다. diff --git a/.proofline/criteria/ac-0030.md b/.proofline/criteria/ac-0030.md new file mode 100644 index 0000000..7303f1d --- /dev/null +++ b/.proofline/criteria/ac-0030.md @@ -0,0 +1,17 @@ +--- +id: "ac-0030" +status: active +--- + +# 데몬 소유자 이전과 기존 배정의 동일 소유자 불변식 + +## Criterion + +portfolio assignment와 daemon owner transfer의 실행 순서와 관계없이 저장된 non-null assignment는 `daemon.owner_user_id == portfolio.owner_user_id`를 만족해야 하며, 참조 중 daemon의 owner transfer는 HTTP 409로 거부되어 daemon owner, portfolio assignment와 broker credentials를 모두 보존해야 한다. + +## Verification + +- 성공 조건: assignment-first에서는 transfer가 409로 실패하고, transfer-first에서는 이전 owner의 assignment가 foreign daemon 404로 실패하며, 미참조 transfer는 성공하고 기존 credential invalidation 정책을 유지한다. +- 실패·경계 조건: foreign owner 관계가 commit되거나 충돌한 transfer가 owner·assignment·credential 중 일부만 변경하거나 race가 timeout·deadlock으로 끝나면 실패한다. +- 검사 방법: migration head가 적용된 disposable PostgreSQL에서 순차 참조 transfer, 미참조 transfer와 양방향 race를 별도 connection/thread로 반복 실행한다. +- 현재 구현 평가: `e90519a`가 daemon `FOR UPDATE` → reference 검사 → credential 삭제 → owner UPDATE 순서를 구현했고 2차 QC에서 integration `12 passed`와 race 반복 `5/5 passed`를 확인했다. diff --git a/.proofline/criteria/ac-0031.md b/.proofline/criteria/ac-0031.md new file mode 100644 index 0000000..d9f9801 --- /dev/null +++ b/.proofline/criteria/ac-0031.md @@ -0,0 +1,17 @@ +--- +id: "ac-0031" +status: active +--- + +# 기존 포트폴리오 mutation 계약과 범위 경계 보존 + +## Criterion + +데몬 배정 기능을 추가해도 기존 portfolio 생성·수정의 인증, target ownership, strategy·asset validation, disabled-state mutation 보호와 service/client token 거부 계약을 유지하고, 승인 범위 밖의 자동 assignment 변경이나 foreign-repository 동작을 도입하지 않아야 한다. + +## Verification + +- 성공 조건: 기존 portfolio CRUD·인증·strategy·asset·disabled-state 회귀 suite가 통과하고 변경 경로가 `portfolio-hub` 내부로 제한된다. +- 실패·경계 조건: 명시적 null 해제, 자동 배정·해제·이전·backfill, `trex-web` 변경 또는 기존 권한 우회가 새로 발생하면 실패한다. +- 검사 방법: 전체 repository regression, main 대비 변경 경로 검사, `trex-web` 범위 부재 확인과 문서 계약 review를 수행한다. +- 현재 구현 평가: 2차 QC 기본 전체 suite `624 passed, 18 skipped`, scope 검사와 독립 review가 통과했으며 `trex-web` 변경은 없다. diff --git a/.proofline/identities.json b/.proofline/identities.json index 92a1f36..d18e04d 100644 --- a/.proofline/identities.json +++ b/.proofline/identities.json @@ -1,5 +1,5 @@ { "schema_version": 1, "next_line_number": 5, - "next_ac_number": 24 + "next_ac_number": 32 } diff --git a/.proofline/lines/line-0004/req-0004.md b/.proofline/lines/line-0004/req-0004.md new file mode 100644 index 0000000..75cc1b5 --- /dev/null +++ b/.proofline/lines/line-0004/req-0004.md @@ -0,0 +1,61 @@ +--- +id: "req-0004" +status: approved +discovery: "dcy-0004" +criteria: + create: + - "ac-0024" + - "ac-0025" + - "ac-0026" + - "ac-0027" + - "ac-0028" + - "ac-0029" + - "ac-0030" + - "ac-0031" + update: [] + retire: [] + satisfy: [] +--- + +# 포트폴리오 데몬 배정 persistence와 소유자 불변식 + +## Objective + +포트폴리오 생성·수정에서 선택한 비-null `managed_by_daemon_id`를 일관되게 검증·저장하고, daemon의 존재·활성·소유자 변경과 경합하더라도 승인된 오류 계약과 동일 소유자 불변식을 원자적으로 보존한다. + +## Scope + +- `POST /v1/portfolios`가 비-null `managed_by_daemon_id`를 생성 persistence에 반영하고 성공 응답과 후속 조회에서 같은 값을 반환한다. +- 데몬 미선택 생성은 기존처럼 `managed_by_daemon_id = null`인 미배정 포트폴리오를 생성한다. +- 생성과 PATCH의 비-null 데몬 지정·재지정은 server-generated UUID 형식, 존재, active 상태와 target portfolio owner 기준 동일 소유자를 검증한다. +- admin이 다른 사용자를 대신해 생성·수정하는 경우 호출자가 아니라 최종 target portfolio owner를 데몬 소유권 비교 기준으로 사용한다. +- malformed UUID는 `422`, 미존재·foreign daemon은 존재를 숨겨 `404`, 소유자 범위의 inactive daemon은 `409`로 실패하고 portfolio와 관련 상태를 부분 변경하지 않는다. +- 데몬 검증과 portfolio write를 하나의 원자적 transaction 및 row-lock 경계로 직렬화한다. +- 데몬 비활성화가 assignment보다 먼저 확정되면 새 assignment를 거부하고, assignment가 먼저 확정된 뒤 비활성화되면 기존 reference를 보존하면서 기존 service-token revoke 계약을 적용한다. +- 참조 중인 daemon의 owner transfer를 `409`로 거부하고 owner, portfolio assignment와 broker credentials를 무변경으로 보존한다. 미참조 daemon owner transfer는 기존 credential invalidation 계약을 유지한다. +- 기존 portfolio mutation의 인증·소유권·strategy·asset·disabled-state 보호를 유지한다. + +## Constraints + +- `managed_by_daemon_id`는 기존 soft reference와 server-generated UUID identity를 유지하며 내부 식별자·테이블 구조를 변경하지 않는다. +- explicit `managed_by_daemon_id: null` PATCH 해제는 이 REQ가 소유하지 않는다. +- 자동 데몬 배정·해제·이전·재배정과 기존 데이터 일괄 backfill을 수행하지 않는다. +- `trex-web` 코드·타입·화면·테스트와 필수 `strategy_id` UI 적응은 범위 밖이다. + +### Current Implementation Assessment + +- 이 REQ와 AC는 요청자의 명시적 지시에 따라 confirmed Discovery와 이미 local `main`에 반영된 구현을 사후 specification으로 포착한다. `approved`/`active`는 normative authority이며 과거 ProofLine implementation chronology, terminal lifecycle closure, 배포 또는 release satisfaction marker가 아니다. +- 제품 구현은 `e2102b6f2b855b7679ab67818202ef706d9848fb`와 1차 QC 보완 `e90519ad3968b48711ced37f42fb45bd883a82af`에 존재하며, evidence 포함 현재 local `main`은 `2abd67da973d334c18d94396292bcc19bd2f81d8`이다. +- 2차 QC에서 집중 API/CRUD/admin daemon `62 passed`, 기본 전체 suite `624 passed, 18 skipped`, migration head 적용 disposable PostgreSQL daemon+strategy integration `12 passed`, assignment/owner-transfer race 반복 `5/5 passed`를 확인했다. +- 변경 production 및 신규 line-0004 테스트 Ruff, `compileall`, `git diff --check`, `proofline validate`가 통과했고 별도 read-only 독립 QC reviewer도 PASS를 기록했다. 기존 admin daemon test의 F401 6건과 deprecation warning은 기준 커밋에 존재한 비차단 debt다. +- push, PR, 원격 main 반영, 운영 DB migration 실행, 배포와 release는 수행되지 않았다. + +## Non-Goals + +- `trex-web` 또는 다른 서비스 변경 +- PATCH의 명시적 `managed_by_daemon_id: null` 해제 지원 +- daemon 실행·중지·재시작이나 배포 자동화 +- 자동 assignment 해제·owner 이전·다른 daemon 재배정·backfill +- 내부 `daemons`·`portfolios` 식별자 또는 테이블 구조 변경 +- push, PR, 원격 main 반영, production migration, 배포 또는 release 승인 +- 사후 specification으로 과거 구현에 ProofLine 정식 delivery chronology나 terminal closure를 소급 부여하는 행위