From 8197f90b24f66cd4dab5c35f756160c5b4b48fef Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 27 Jul 2026 17:42:23 +0900 Subject: [PATCH] =?UTF-8?q?docs(ai-character):=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9E=90=20API=20=EA=B3=84=ED=9A=8D=EC=9D=84=20=EB=B3=B4?= =?UTF-8?q?=EA=B0=95=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20260724_AI캐릭터_관리자_API/plan-task.md | 917 +++++++++++++++++- docs/20260724_AI캐릭터_관리자_API/prd.md | 4 + 2 files changed, 875 insertions(+), 46 deletions(-) diff --git a/docs/20260724_AI캐릭터_관리자_API/plan-task.md b/docs/20260724_AI캐릭터_관리자_API/plan-task.md index 7ea8ced6..bc897c7b 100644 --- a/docs/20260724_AI캐릭터_관리자_API/plan-task.md +++ b/docs/20260724_AI캐릭터_관리자_API/plan-task.md @@ -8,6 +8,37 @@ **Tech Stack:** Kotlin, Spring Boot 2.7.14, Java 17, Spring Security, JPA/Hibernate, QueryDSL, MySQL, Gradle Wrapper, JUnit5. +| 문서 항목 | 내용 | +|---|---| +| 상태 | 구현 중 | +| 작성일 | 2026-07-24 | +| 요구사항 기준 | `docs/20260724_AI캐릭터_관리자_API/prd.md` | +| API 기준 | 이 문서의 `Endpoint Contract Summary` | +| 현재 Phase | Phase 2~3 재검토 및 보완 | +| 현재 활성 Goal | 없음 (`P2-R1`부터 시작) | + +## 현재 상태 + +| Phase | 상태 | 기존 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 | +|---:|---|---:|---|---| +| 1 | 완료 | 7/7 | 완료 | 없음 | +| 2 | 진행 중 | 2/2 | `P2-R1` | `REV-001`~`REV-003`, `REV-007`, `REV-008` 보완 후 `P2-GATE` | +| 3 | 진행 중 | 2/2 | `P3-R1` | `REV-004`~`REV-008` 보완 후 `P3-GATE` | +| 4 | 대기 | 0/6 | `P4-T1` | `P3-GATE` | +| 5 | 대기 | 0/6 | `P5-T1` | `P4-GATE` | +| 6 | 대기 | 0/4 | `P6-T1` | `P5-GATE` | +| 7 | 대기 | 0/2 | `P7-T1` | Phase 1~6 Gate 완료 | + +- Phase는 결과와 의존성을 묶는 문서 단위다. `create_goal`에는 Task 또는 Phase Gate 하나만 등록한다. +- 동시에 하나의 미완료 goal만 운용하고, 사용자가 명시적으로 요청하지 않으면 token budget을 설정하지 않는다. +- 기존 `[x]` Task와 검증 기록은 당시 완료 이력으로 보존한다. 후속 리뷰에서 발견한 문제는 기존 Task를 다시 열지 않고 새 review/fix + Goal로 처리한다. +- 각 Goal은 체크박스, focused test, 완료 증거와 Progress 기록이 모두 충족된 뒤에만 `complete`로 갱신한다. +- 검증은 focused test와 영향받는 slice/legacy 회귀를 우선한다. 전체 `./gradlew test`는 공통 경계·여러 Phase 영향, targeted + 결과만으로 영향 범위를 판단할 수 없는 실패 또는 최종 release 판정에 실제로 필요하다고 기록한 경우에만 실행한다. +- 전체 회귀를 생략하면 생략 근거와 대신 실행한 focused/영향 범위 회귀 명령을 Progress와 검증 기록에 남긴다. +- 같은 차단 사유가 최초 시도와 자동 후속을 포함해 3회 연속 반복되고, 문서화나 독립 작업도 불가능할 때만 `blocked`로 갱신한다. + --- ## Source of Truth @@ -200,7 +231,7 @@ Query parameters: ```json { "search": "밤", - "status": "OPEN", + "status": "OPEN | SCHEDULED", "page": 0, "size": 20 } @@ -272,6 +303,7 @@ Form fields: "price": 1000, "isAdult": false, "isActive": true, + "themeId": 11, "releaseDateUtc": "2026-07-25T00:00:00Z", "seriesIds": [701] } @@ -291,7 +323,7 @@ Form fields: ```json { "coverImage": "File | optional", - "audioFile": "File | optional", + "audioFile": "미지원", "request": { "title": "밤 산책 수정", "description": "수정된 설명", @@ -969,14 +1001,23 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 #### 권장 commit 경계 - `feat: add ai character admin character slice` -- [ ] **Task 2.1: 기존 character parity 특성화 baseline 고정** +- [x] **Task 2.1: 기존 character parity 특성화 baseline 고정** + - **Goal 이력 `P2-H1`:** 기존 character admin 동작을 신규 v2 구현의 비교 기준으로 고정했다. + - **완료 증거:** 아래 특성화 테스트 RED/GREEN/REFACTOR 기록과 실제 명령 결과. + - **범위 밖:** 신규 v2 endpoint 구현. - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/LegacyChatCharacterAdminCharacterizationTest.kt` - CHARACTERIZE: 기존 character admin 구현을 대상으로 중복 이름, 외부 API·S3·event 호출/실패 순서와 transaction/compensation, original work 연결, 언어 감지/번역 이벤트, creatorMember 표시 정보 동기화, `isActive=false` 및 연결 Member/콘텐츠 미삭제 결과를 고정한다. - BASELINE: 신규 v2 production code 변경 전에 특성화 테스트가 기존 구현을 대상으로 통과함을 확인한다. - REFACTOR: fixture와 assertion naming만 정리하고 parity baseline은 변경하지 않는다. - Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.LegacyChatCharacterAdminCharacterizationTest` + - 검증 기록(RED): 무엇: 기존 character admin baseline 특성화 테스트. 왜: 신규 v2 production code 전에 기존 생성/수정/비활성화/원작/언어 이벤트/creatorMember·콘텐츠 보존 계약을 고정하기 위해. 어떻게: 위 테스트를 추가하고 동일 명령을 실행했다. 결과: 이벤트 baseline 추가 직후 Mockito/S3 stub과 request JSON 누락으로 2회 실패해 RED를 확인했다. + - 검증 기록(GREEN): 무엇: Task 2.1 baseline 통과. 왜: 기존 구현이 현재 특성화 계약을 만족하는지 확인하기 위해. 어떻게: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.LegacyChatCharacterAdminCharacterizationTest`를 실행했다. 결과: `BUILD SUCCESSFUL`을 확인했다. + - 검증 기록(REFACTOR): 무엇: Kotlin style과 리뷰 gate. 왜: 테스트-only baseline의 품질과 계획 준수 여부를 확인하기 위해. 어떻게: `./gradlew ktlintCheck`와 spec/code-quality read-only review를 실행했다. 결과: `BUILD SUCCESSFUL`, 두 리뷰 모두 `APPROVED`였다. -- [ ] **Task 2.2: character controller/facade/DTO 구현** +- [x] **Task 2.2: character controller/facade/DTO 구현** + - **Goal 이력 `P2-H2`:** 캐릭터 목록·상세·생성·수정의 최초 v2 API 구현을 제공했다. + - **완료 증거:** 아래 controller test와 `ktlintCheck` 기록. 이 이력만으로 Phase 2 Gate 통과를 의미하지 않는다. + - **범위 밖:** 후속 심층 리뷰에서 확정되는 누락·회귀 보완. - Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/*` - RED: Task 2.1 baseline에서 옮긴 `AiCharacterAdminCharacterServiceTest`와 controller 권한/페이지네이션 경계 테스트를 작성하고 신규 v2 미구현으로 실패함을 확인한다. - RED: 현재 신규 dependency 금지 제약에 따라 기존 Spring binding 또는 수동 validation 전략을 우선하고, 실제 DTO의 invalid @@ -985,6 +1026,163 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 - GREEN: endpoint contract summary의 character endpoint를 구현하고 목록/검색 `page/size` 기본값·최소·최대 보정을 적용한다. - REFACTOR: 신규 DTO가 legacy/public DTO를 외부 계약으로 재노출하지 않는지 확인하고 회귀 테스트를 재실행한다. - Verify: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.*'` + - 검증 기록(RED): 무엇: v2 character controller 계약 테스트 7개. 왜: 신규 route가 목록/검색/상세/생성/수정과 ADMIN 경계를 제공하지 않음을 확인하기 위해. 어떻게: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.AiCharacterAdminCharacterControllerTest`를 실행했다. 결과: 모든 기대 endpoint가 미구현 route의 404를 반환해 7개가 실패했다. + - 검증 기록(GREEN): 무엇: 목록/검색 page-size 보정, target resolver 상세 거부, multipart 생성/수정, AI creatorMember 동기화, soft delete, invalid JSON, ADMIN 인가. 왜: Task 2.2 API 계약과 레거시 부작용 재사용을 확인하기 위해. 어떻게: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.*'`를 실행했다. 결과: controller 8개와 Task 2.1 baseline 7개가 모두 통과했다. + - 검증 기록(REFACTOR): 무엇: 신규 v2 DTO 경계와 Kotlin style. 왜: legacy/public DTO 비노출과 코드 스타일을 확인하기 위해. 어떻게: `./gradlew ktlintCheck`를 실행했다. 결과: `BUILD SUCCESSFUL`을 확인했다. + +- [ ] **Task 2.3: Phase 2 요구사항·계약·코드 리뷰** + +**Goal 실행 `P2-R1`:** PRD Feature B와 Endpoint Contract Summary를 Phase 2 코드·테스트에 추적해 확정된 누락만 후속 Goal로 전환한다. + +- **추적 review ID:** `REV-001`, `REV-002`, `REV-003`, `REV-007`, `REV-008`. +- **시작 조건:** `P2-H1`, `P2-H2` 산출물과 검증 기록 존재. +- **완료 증거:** `docs/sample/sample-review.md` 형식의 리뷰 문서, endpoint/side-effect 추적표, 모든 후보의 확정·오탐·보류 판정, + 후속 Goal 연결과 Progress 기록. +- **범위 밖:** 리뷰 도중 production code 수정, Phase 3 이후 기능 검토. +- **TDD 예외 사유:** 구현이 아닌 read-only 리뷰 Task다. +- **대체 검증 방법:** PRD·계약·production·test를 대조하고 현재 focused test를 실행해 관찰 결과를 리뷰 문서에 기록한다. + +**Files:** + +- Create: `docs/20260724_AI캐릭터_관리자_API/reviews/phase2-character-review.md` +- Confirm: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterController.kt` +- Confirm: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterFacade.kt` +- Confirm: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterDto.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterControllerTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterControllerMutationTest.kt` + +- [ ] 목록·검색·상세·생성·수정·비활성화 endpoint와 DTO 필드를 PRD/계약에 1:1로 추적한다. +- [ ] 중복 이름, 외부 API, S3, 원작, 언어 이벤트, creatorMember 동기화와 실패 순서를 코드·test에 추적한다. +- [ ] ADMIN 이중 인가, pagination, multipart/binding, KO/EN/JA 오류, private 정보 비노출 계약을 확인한다. +- [ ] `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.*'`의 실제 결과를 기록한다. +- [ ] 후보를 확정·오탐·보류로 판정하고 확정 항목을 아래 세부 Goal에 연결하거나 새 회귀 수정 Goal을 계획에 먼저 추가한다. + +- [ ] **Task 2.4: 캐릭터 목록·검색·상세 보완** + +**Goal 실행 `P2-T3`:** 캐릭터 조회 API의 검색·pagination·응답·target 계약을 독립적으로 검증하고 확정된 누락을 최소 수정한다. + +- **추적 review ID:** `REV-007` 중 목록 응답 field set과 DTO 경계. +- **시작 조건:** `P2-R1` 완료와 관련 review ID 확정. 확정 finding이 없으면 Decision Log에 `해당 없음` 근거를 남긴다. +- **완료 증거:** 조회 전용 RED/GREEN, focused test, DTO 비노출 점검과 Progress 기록. +- **범위 밖:** 생성, 수정, 외부 API·S3 mutation. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterController.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterDto.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterControllerTest.kt` + +- [ ] **RED:** 목록 item의 exact JSON key를 고정하고 상세 전용 `creatorProfileImageUrl`, `creatorIntroduce`, `updatedAtUtc`가 노출되는 현재 동작을 실패로 재현한다. +- [ ] **RED 확인:** `page=0`, `size` 기본 20·최소 20·최대 50, 검색·`hasNext`와 상세 target 불변식의 경계 test를 실행해 의도한 assertion 실패를 확인한다. +- [ ] **GREEN:** 목록 전용 DTO와 mapper를 최소 구현해 계약 field만 반환하고 credential·token·private path를 노출하지 않는다. +- [ ] **GREEN 확인:** 같은 focused test를 다시 실행해 exact 목록 field set과 pagination·target 계약이 모두 통과하는지 확인한다. +- [ ] **REFACTOR:** 상세 UTC field 계약을 유지하면서 목록/상세 DTO 의존 방향을 점검하고 focused test와 `ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.AiCharacterAdminCharacterControllerTest` + +- [ ] **Task 2.5: 캐릭터 생성 흐름 보완** + +**Goal 실행 `P2-T4`:** 캐릭터 생성의 중복 검증, 외부 API, 이미지, 원작, creatorMember와 이벤트 흐름을 parity 기준으로 완결한다. + +- **추적 review ID:** `REV-002`, `REV-003`, `REV-007` 중 생성 request·`characterType` 계약. +- **시작 조건:** `P2-T3` 완료와 생성 관련 확정 review ID. +- **완료 증거:** 정상 생성 및 실패 지점별 RED/GREEN, DB/S3/외부 API/event 결과, focused/legacy test와 Progress 기록. +- **범위 밖:** 기존 external character API 계약 변경, 캐릭터 수정·비활성화. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterDto.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterExternalApiClient.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterImageStorage.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterControllerMutationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/LegacyChatCharacterAdminCharacterizationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/LegacyChatCharacterAdminCharacterControllerEventCharacterizationTest.kt` + +- [ ] `systemPrompt`, `externalCharacterId`, 생성 `isActive`와 invalid `characterType`의 canonical request 계약을 Endpoint Contract Summary·legacy 특성화 결과로 확정하고 충돌 시 코드 수정 전에 Decision Log를 갱신한다. +- [ ] **RED:** 확정된 문서 JSON의 역직렬화·반영, 동시 중복 이름 생성, 외부 API 실패, S3 실패와 존재하지 않는 `originalWorkId` 실패를 각각 재현한다. +- [ ] **RED 확인:** 실패 지점별 DB row·creatorMember·원작 연결·S3 객체·외부 캐릭터·event 결과와 호출 순서를 단언해 현재 부분 저장 또는 고아 부작용을 확인한다. +- [ ] **GREEN:** 모든 DB 참조를 외부 부작용 전에 검증하고, 동시 중복 정책과 legacy parity에 맞는 최소 보상/정리 경계로 정상 생성과 실패 원자성을 통과시킨다. +- [ ] **GREEN 확인:** 같은 생성 focused/characterization test를 다시 실행해 정상 결과와 실패 지점별 잔존 상태가 확정 계약과 일치하는지 확인한다. +- [ ] **REFACTOR:** creatorMember 표시 정보와 언어 이벤트를 포함한 focused/legacy characterization test 및 `ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.AiCharacterAdminCharacterControllerMutationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.LegacyChatCharacterAdminCharacterizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.LegacyChatCharacterAdminCharacterControllerEventCharacterizationTest` + +- [ ] **Task 2.6: 캐릭터 수정·비활성화 흐름 보완** + +**Goal 실행 `P2-T5`:** 캐릭터 수정과 `isActive=false`가 표시 정보를 동기화하고 연결 Member·콘텐츠를 보존하도록 완결한다. + +- **추적 review ID:** `REV-002`, `REV-003`, `REV-007` 중 수정 request·응답·soft-delete parity. +- **시작 조건:** `P2-T4` 완료와 수정·비활성화 관련 확정 review ID. +- **완료 증거:** 수정·이미지 유지/교체·soft delete RED/GREEN, 보존/no-partial-update 검증과 Progress 기록. +- **범위 밖:** hard delete, cascade delete, 복원 API. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterDto.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterExternalApiClient.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterImageStorage.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterMapper.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterControllerMutationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/LegacyChatCharacterAdminCharacterizationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/LegacyChatCharacterAdminCharacterControllerEventCharacterizationTest.kt` + +- [ ] `externalCharacterId`, 일반 수정과 `isActive=false` 혼합 요청, invalid `characterType`의 canonical update 계약을 확정하고 충돌 시 Decision Log를 먼저 갱신한다. +- [ ] **RED:** 문서 PUT JSON의 field 반영, soft delete와 일반 수정 혼합, image 동시 요청, 외부 수정 성공 후 S3/DB 실패, `updatedAtUtc`의 flush 전 mapping을 각각 재현한다. +- [ ] **RED 확인:** soft delete 성공·실패에서 row·Member·콘텐츠 보존, 미참조 S3 객체 0건, 외부/DB 상태 일치와 응답 timestamp가 후속 GET과 같은지 확인한다. +- [ ] **GREEN:** 확정 계약에 맞춰 혼합 요청을 명시적으로 처리하고, 불필요한 upload를 차단하며 외부/S3/DB 보상 경계와 flush 후 response mapping을 최소 구현한다. +- [ ] **GREEN 확인:** 같은 수정 focused/characterization test를 다시 실행해 field 반영, 보상 결과, soft-delete 보존과 timestamp가 모두 통과하는지 확인한다. +- [ ] **REFACTOR:** creatorMember 표시 정보·번역 event와 legacy `characterType` 동작을 포함한 focused/characterization test 및 `ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.AiCharacterAdminCharacterControllerMutationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.LegacyChatCharacterAdminCharacterizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.LegacyChatCharacterAdminCharacterControllerEventCharacterizationTest` + +- [ ] **Task 2.7: Phase 2 보안·오류·회귀 보완** + +**Goal 실행 `P2-T6`:** Phase 2의 모든 endpoint가 공통 ADMIN·오류·CORS 계약을 공유하고 legacy/public 계약을 회귀시키지 않음을 고정한다. + +- **추적 review ID:** `REV-001`, `REV-007`, `REV-008`. +- **시작 조건:** `P2-T5` 완료 또는 앞선 Goal의 근거 있는 `해당 없음` 판정. +- **완료 증거:** endpoint 권한 매트릭스, 정확한 오류 status/key/KO·EN·JA, legacy 회귀와 Progress 기록. +- **범위 밖:** Phase 1 공통 security/error 구조 재설계, Phase 3 기능. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterController.kt` +- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterControllerTest.kt` +- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/character/AiCharacterAdminCharacterControllerMutationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/AiCharacterAdminAuthorizationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/AiCharacterAdminErrorContractTest.kt` + +- [ ] **RED:** `/api/v2/admin/ai-characters/unmapped-path`가 detail `Long` binding에 잡혀 404 대신 400이 되는 KO/EN/JA·허용 Origin CORS 4건을 현재 Phase 1 오류 계약 test로 재현한다. +- [ ] **RED:** 목록·상세·생성·수정 각각의 JWT role × DB role, stale ADMIN claim과 binding·multipart·domain/client/server 오류의 exact status/key/KO·EN·JA를 parameterized test로 고정한다. +- [ ] **RED 확인:** 오류 계약과 실제 endpoint matrix를 실행해 404 회귀 4건과 누락된 인가·i18n assertion이 의도대로 실패하는지 확인한다. +- [ ] **GREEN:** numeric `characterId`만 resource handler에 매핑되도록 최소 수정하고, Phase 2 오류 의미를 확정된 message key와 `ApiResponse.error`로 반환한다. +- [ ] **GREEN 확인:** 같은 오류·인가 focused test를 다시 실행해 실제 endpoint의 status/header/envelope와 KO/EN/JA가 모두 통과하는지 확인한다. +- [ ] **REFACTOR:** 실제 test 파일 목록과 targeted 명령을 대조해 존재하지 않는 `AiCharacterAdminCharacterServiceTest` 참조 및 과거 test 수 기록은 삭제하지 않고 정정 기록을 누적한다. +- [ ] 기존 admin/public character contract, 신규 DTO 의존 방향과 Phase 2 focused test·`ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.*' --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminAuthorizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTest` + +#### Phase 2 Gate + +**Goal 실행 `P2-GATE`:** Phase 2 캐릭터 관리의 PRD 추적성, 정상·실패 흐름과 legacy 회귀를 최종 판정한다. + +- [ ] **`P2-GATE` 완료:** 시작 조건과 완료 증거를 모두 충족하고 현재 상태표와 Progress를 갱신한다. + +- **시작 조건:** `P2-R1`, `P2-T3`~`P2-T6` 완료 또는 근거 있는 `해당 없음` 판정. +- **완료 증거:** 아래 명령 성공, review 후보 0건, 확정 finding 처리 완료와 Progress 기록. +- **범위 밖:** Gate 통과를 위한 test 삭제·완화, Phase 3 기능 수정. + +- [ ] `REV-001`~`REV-003`, `REV-007`, `REV-008`의 계약 결정·failure matrix·수정 test와 실제 결과가 각 소유 Goal의 Progress에 연결됐다. +- [ ] 캐릭터 목록 exact key, 문서 mutation JSON, 동시 중복 결과, 외부/S3/DB 보상, post-flush `updatedAtUtc`와 실제 endpoint 권한·i18n matrix에 미결정 항목이 없다. +- [ ] 완료 이력의 누락 test 파일·test 수·failure-order 증거는 원문을 삭제하지 않고 최신 정정 기록으로 재현 가능하게 남겼다. + +```bash +./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.*' +./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminAuthorizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTest +./gradlew ktlintCheck +``` + +**Expected:** 모든 명령 exit code 0, Phase 2 review의 후보·확정 미처리 항목 0건, 관련 legacy/public 계약 diff 없음. --- @@ -1002,11 +1200,31 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 - 콘텐츠 생성/수정/delete 기존 동작 특성화 테스트. #### API endpoint와 request/response contract +- `GET /api/v2/admin/ai-characters/audio-content-themes` + - Request: query/body 없음. + - Response `data`: + + ```json + [ + { + "themeId": 11, + "themeName": "ASMR", + "imageUrl": "https://cdn.example.com/audio-content-theme/asmr.png" + } + ] + ``` + + - 기존 크리에이터 관리자 콘텐츠 등록 화면의 콘텐츠 테마(카테고리) 조회와 같은 기능이다. + - 기존 내부/legacy DTO의 `id`, `theme`, `image` 필드명은 frontend 계약으로 노출하지 않고, 신규 v2 DTO의 `themeId`, `themeName`, `imageUrl`만 사용한다. - `GET /api/v2/admin/ai-characters/{characterId}/audio-contents` - `GET /api/v2/admin/ai-characters/{characterId}/audio-contents/{contentId}` - `POST /api/v2/admin/ai-characters/{characterId}/audio-contents` + - multipart `coverImage`, `audioFile`, `request` JSON string part를 사용한다. + - `request` JSON은 `title`, `description`, `tags`, `price`, `purchaseOption`, `limited`, `isAdult`, `isActive`, `themeId`, `releaseDateUtc?`, `seriesIds`, `isGeneratePreview`, `isOnlyRental`, `isPointAvailable`, `isCommentAvailable`, `isFullDetailVisible`, `previewStartTime`, `previewEndTime`, `languageCode`를 포함한다. + - legacy `CreateAudioContentRequest`의 `detail`은 v2 `description`, `releaseDate`는 UTC ISO-8601 `releaseDateUtc`로 받으며 facade에서 기존 pipeline 입력으로 변환한다. - `PUT /api/v2/admin/ai-characters/{characterId}/audio-contents/{contentId}` -- response item/detail에는 `contentId`, `title`, `status`, `isActive`, `releaseDate`, `coverImageUrl`, `audioSignedUrl?`, 가격/성인/공개 상태 등 기존 관리자 화면 필드를 포함한다. +- `audioFile` 교체는 기존 creator/admin 수정 pipeline에 없는 동작이므로 Phase 3 범위에서는 제공하지 않는다. 오디오 파일 교체가 필요하면 별도 upload/processing parity 설계 후 추가한다. +- response item은 현 v2 목록 계약을 유지한다. response detail에는 기존 `GetAudioContentDetailResponse`의 필드 전체를 포함하고, `description`, `audioSignedUrl`, `releaseDateUtc`, `seriesIds`, `createdAtUtc`, `updatedAtUtc` 같은 v2 관리자 필드도 유지한다. 구매·좋아요·핀·추천·댓글 목록처럼 viewer 상태가 필요한 legacy 상세 필드는 관리자 상세에서 안전한 기본값을 반환한다. #### entity, repository, service 변경 - Entity: 변경 없음. @@ -1047,20 +1265,211 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 #### 권장 commit 경계 - `feat: add ai character admin content slice` -- [ ] **Task 3.1: 기존 콘텐츠와 signed URL 특성화 baseline 고정** +- [x] **Task 3.1: 기존 콘텐츠와 signed URL 특성화 baseline 고정** + - **Goal 이력 `P3-H1`:** 기존 creator/admin 콘텐츠와 signed URL 동작을 신규 v2 비교 기준으로 고정했다. + - **완료 증거:** 아래 특성화 테스트와 `ktlintCheck` 실행 기록. + - **범위 밖:** 신규 v2 content endpoint 구현. - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/LegacyCreatorAdminAudioContentCharacterizationTest.kt`, `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AudioContentCloudFrontCharacterizationTest.kt` - CHARACTERIZE: 기존 creator/admin content 구현을 대상으로 검증, 파일 처리, 가격, 공개/예약, 번역/알림, soft delete, upload/processing pipeline 결과를 고정한다. - CHARACTERIZE: 기존 creator admin signed URL 만료 계산식과 만료 계산·path 처리에서 실제로 관찰되는 edge case, private path 비노출 계약을 고정한다. - BASELINE: 신규 v2 production code 변경 전에 두 특성화 테스트가 기존 구현을 대상으로 통과함을 확인한다. - REFACTOR: 테스트 fixture만 정리하고 content/signed URL parity baseline은 변경하지 않는다. - Verify: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*CharacterizationTest'` + - 검증 기록 (2026-07-26): 위 특성화 테스트 6건과 `./gradlew ktlintCheck`가 모두 통과했다. -- [ ] **Task 3.2: content controller/facade/DTO 구현** +- [x] **Task 3.2: content controller/facade/DTO 구현** + - **Goal 이력 `P3-H2`:** 콘텐츠 조회·생성·수정의 최초 v2 API 구현을 제공했다. + - **완료 증거:** 아래 RED/GREEN 및 후속 보완 기록. 이 이력만으로 Phase 3 Gate 통과를 의미하지 않는다. + - **범위 밖:** 후속 심층 리뷰에서 확정되는 누락·회귀 보완. - Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/*` - RED: Task 3.1 baseline에서 옮긴 content parity, cross-character content 접근, signed URL/private path 계약 테스트와 controller 권한/페이지네이션 경계 테스트를 작성하고 신규 v2 미구현으로 실패함을 확인한다. - GREEN: content endpoint와 owner-scoped query/write를 구현하고 목록/검색 `page/size` 기본값·최소·최대 보정을 적용한다. - REFACTOR: signed URL/private path mapping과 기존 pipeline 재사용 경계를 정리하고 회귀 테스트를 재실행한다. - Verify: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*'` + - 구현 기록(2026-07-26): `GET` 목록/검색·상세와 `PUT` 수정/soft delete를 target resolver와 owner-scoped query로 구현했다. 응답은 v2 DTO만 사용하며 private content path 필드를 반환하지 않는다. + - 구현 보완(2026-07-27): `POST` 생성을 기존 `AudioContentService.createAudioContent` 재사용 방식으로 제공한다. v2 adapter에서 `themeId`, ISO-8601 `releaseDateUtc`, owner-scoped `seriesIds` 연결을 처리하고, 업로드 완료 전 `isActive` 직접 활성화는 기존 processing pipeline parity를 위해 수행하지 않는다. + - 검증 기록(RED, 2026-07-26): `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentControllerTest'`를 실행했고, 신규 route 부재로 목록은 404, 상세와 PUT은 404/405여서 의도대로 실패함을 확인했다. + - 검증 기록(GREEN, 2026-07-26): `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*'`와 `./gradlew ktlintCheck`를 실행해 모두 `BUILD SUCCESSFUL`을 확인했다. + - 검토 보완(2026-07-26): 상세 응답에 target owner 범위의 활성 series ID 목록을 추가하고, 다른 캐릭터 콘텐츠 PUT이 DB를 변경하지 않는 통합 테스트를 추가했다. + - 재검증(2026-07-26): `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*'`를 실행해 `BUILD SUCCESSFUL`을 확인했다. + - 검토 보완(2026-07-27): 생성/수정의 `seriesIds` owner-scoped 연결, 생성의 ISO-8601 `releaseDateUtc` 변환, POST 제공 문서 불일치를 보완했다. 수정 `audioFile` 교체는 기존 creator/admin 수정 pipeline에 없어 Phase 3에서 미지원으로 명시했다. + +- [ ] **Task 3.3: Phase 3 요구사항·계약·코드 리뷰** + +**Goal 실행 `P3-R1`:** PRD Feature C와 Endpoint Contract Summary를 콘텐츠 코드·테스트에 추적해 확정된 누락만 후속 Goal로 전환한다. + +- **추적 review ID:** `REV-004`, `REV-005`, `REV-006`, `REV-007`, `REV-008`. +- **시작 조건:** `P2-GATE`, `P3-H1`, `P3-H2` 완료 증거 존재. +- **완료 증거:** `docs/sample/sample-review.md` 형식의 리뷰 문서, endpoint·pipeline·side-effect 추적표, 모든 후보 판정과 Progress 기록. +- **범위 밖:** 리뷰 도중 production code 수정, series/community 기능 검토. +- **TDD 예외 사유:** 구현이 아닌 read-only 리뷰 Task다. +- **대체 검증 방법:** PRD·계약·production·test를 대조하고 focused test를 실행해 실제 결과를 리뷰 문서에 기록한다. + +**Files:** + +- Create: `docs/20260724_AI캐릭터_관리자_API/reviews/phase3-audio-content-review.md` +- Confirm: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentController.kt` +- Confirm: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentFacade.kt` +- Confirm: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentDto.kt` +- Confirm: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentControllerTest.kt` + +- [ ] 테마·목록·검색·상세·생성·수정·soft delete endpoint와 DTO 필드를 PRD/계약에 추적한다. +- [ ] signed URL TTL/path, private path 비노출, viewer 상태 기본값을 production·test에 추적한다. +- [ ] 생성/update pipeline, 파일, 가격, 공개·예약, 번역·알림, `seriesIds`, 날짜 변환과 실패 순서를 확인한다. +- [ ] owner 검증, no-side-effect, ADMIN 인가, 오류 i18n, pagination/multipart 계약을 확인한다. +- [ ] `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*'` 결과와 후보 판정을 리뷰 문서에 기록한다. + +- [ ] **Task 3.4: 활성 콘텐츠 테마 API 보완** + +**Goal 실행 `P3-T3`:** 활성 콘텐츠 테마를 전용 `themeId/themeName/imageUrl` DTO로 반환하는 관리자 API를 완결한다. + +- **추적 review ID:** `REV-007`, `REV-008` 중 v2 DTO 경계와 endpoint 인가. +- **시작 조건:** `P3-R1` 완료와 테마 endpoint review 판정. +- **완료 증거:** request body 없는 GET, 활성 필터, DTO field contract RED/GREEN과 Progress 기록. +- **범위 밖:** 테마 CRUD, legacy DTO 외부 노출. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentController.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentDto.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentFacade.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentThemeControllerTest.kt` + +- [ ] **RED:** 테마 endpoint 부재 또는 계약 불일치와 legacy field 노출을 재현하는 가장 작은 실패 test를 작성한다. +- [ ] **RED 확인:** focused test를 실행해 의도한 route·field assertion 실패를 확인한다. +- [ ] **GREEN:** 활성 테마만 `themeId`, `themeName`, `imageUrl`로 반환하는 최소 구현을 작성한다. +- [ ] **GREEN 확인:** request body 없음, exact field set과 ADMIN 이중 인가를 포함한 focused test 성공을 확인한다. +- [ ] **REFACTOR:** v2 DTO 경계만 정리하고 테마 focused test와 `ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentThemeControllerTest` + +- [ ] **Task 3.5: 콘텐츠 목록·상세·signed URL 보완** + +**Goal 실행 `P3-T4`:** owner-scoped 콘텐츠 조회와 signed URL·상세 DTO 계약을 독립적으로 완결한다. + +- **추적 review ID:** `REV-005`, `REV-007` 중 상세 parity와 v2 전용 DTO 경계. +- **시작 조건:** `P3-T3` 완료와 조회/signed URL 관련 review 판정. +- **완료 증거:** 검색·status·pagination·상세·TTL/path/private 정보 RED/GREEN과 Progress 기록. +- **범위 밖:** 콘텐츠 생성·수정, community audio 30분 정책. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentDto.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentMapper.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentQueryTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AudioContentCloudFrontCharacterizationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentServiceTest.kt` + +- [ ] **RED:** `purchaseOption=RENT_ONLY`, entity `isOnlyRental=true`, 미래·과거 `releaseDate` 조합에서 legacy 상세와 다른 `isOnlyRental`, `purchaseOption`, `releaseDate`를 재현한다. +- [ ] **RED:** 응답의 creator·buyer·other content·comment·translation 중첩 타입이 legacy/public DTO package에 직접 의존하는 현재 경계를 검출하고 exact JSON key를 고정한다. +- [ ] **RED 확인:** query/legacy baseline test를 실행해 세 compatibility field와 금지 DTO 의존이 의도대로 실패하는지 확인한다. +- [ ] **GREEN:** legacy 파생 규칙과 현지화된 `releaseDate` 의미를 유지하고 UTC 원본은 `releaseDateUtc`에만 반환하며, 동일 JSON을 v2 전용 중첩 DTO로 최소 매핑한다. +- [ ] **GREEN 확인:** 같은 query/legacy baseline test를 다시 실행해 legacy compatibility field, `releaseDateUtc`와 exact JSON schema가 모두 통과하는지 확인한다. +- [ ] **REFACTOR:** owner·검색·status·pagination, 활성 owner-scoped `seriesIds`, viewer 기본값, signed URL TTL/path와 private 정보 비노출을 함께 회귀한다. +- [ ] 조회/signed URL focused test와 `ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentQueryTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AudioContentCloudFrontCharacterizationTest --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest` + +- [ ] **Task 3.6: 콘텐츠 생성 pipeline 보완** + +**Goal 실행 `P3-T5`:** 콘텐츠 생성의 multipart 입력, legacy field 변환, 파일·processing·series 연결과 side effect parity를 완결한다. + +- **추적 review ID:** `REV-006`, `REV-007`, `REV-008` 중 생성 binding·field·pipeline 특성화. +- **시작 조건:** `P3-T4` 완료와 생성 pipeline 관련 review 판정. +- **완료 증거:** 전체 생성 field·파일·날짜·series RED/GREEN, 실패 순서와 focused/legacy test 기록. +- **범위 밖:** upload/processing pipeline 정책 변경, 오디오 파일 교체. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentController.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentDto.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentCreateTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/LegacyCreatorAdminAudioContentCharacterizationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/AiCharacterAdminErrorContractTest.kt` + +- [ ] `tags` 필수 여부와 생성 `isActive=false`의 canonical 계약을 legacy pipeline·Endpoint Contract Summary로 확정하고 충돌 시 코드 수정 전에 Decision Log를 갱신한다. +- [ ] **RED:** `coverImage`, `audioFile`, `request` 각 part 누락에서 Kotlin nullable 때문에 `MissingServletRequestPartException`이 발생하지 않는 현재 binding과 KO/EN/JA envelope 차이, facade·DB·S3·event 호출 0건 기대를 재현한다. +- [ ] **RED:** 생성 request 전체 field, `description/releaseDateUtc` 변환, `tags` 누락, `isActive=false`, target·theme·`seriesIds` 오류와 S3/processing/event 실패 순서를 각각 고정한다. +- [ ] **RED 확인:** create/error/legacy characterization test를 실행해 part별 exception·field 계약·failure order가 의도대로 실패하는지 확인한다. +- [ ] **GREEN:** 필수 file part를 non-null binding으로 만들고 확정된 field 계약, 외부 부작용 전 참조 검증과 legacy upload/processing parity를 최소 구현한다. +- [ ] **GREEN 확인:** 같은 test를 다시 실행해 part별 400/i18n, 정상 생성과 실패 후 DB/S3/event 결과가 모두 통과하는지 확인한다. +- [ ] **REFACTOR:** cover/audio upload, 가격·공개·예약·번역·알림 및 실패 후 DB/S3/event 결과를 characterization/focused test로 회귀하고 `ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentCreateTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.LegacyCreatorAdminAudioContentCharacterizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTest` + +- [ ] **Task 3.7: 콘텐츠 수정·soft delete 보완** + +**Goal 실행 `P3-T6`:** 콘텐츠 수정, cover 유지/교체, series 재연결과 soft delete를 owner-safe하게 완결한다. + +- **추적 review ID:** `REV-004`. +- **시작 조건:** `P3-T5` 완료와 수정·삭제 관련 review 판정. +- **완료 증거:** 수정·soft delete·audioFile 미지원·cross-owner RED/GREEN과 Progress 기록. +- **범위 밖:** audio file 교체, hard delete. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentRepository.kt` +- Confirm: `src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/SeriesContent.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentUpdateTest.kt` + +- [ ] **RED:** 기존 `SeriesContent.orders`, row ID, `createdAt`이 있는 콘텐츠에 동일 `seriesIds`를 PUT했을 때 전부 삭제·재생성되는 현재 동작을 실패 test로 고정한다. +- [ ] **RED 확인:** 동일 ID, 추가 ID, 제거 ID를 각각 요청해 교집합 metadata 보존과 차집합만 insert/delete한다는 기대가 현재 실패하는지 확인한다. +- [ ] **GREEN:** 기존 연결과 요청 ID의 차집합만 변경하고 교집합 row의 ID·`orders`·`createdAt`을 보존하는 최소 구현을 작성한다. +- [ ] **GREEN 확인:** 같은 update test를 다시 실행해 동일 집합 no-op, 교집합 metadata 보존과 차집합 변경만 발생하는지 확인한다. +- [ ] **REFACTOR:** cover 유지/교체, 날짜, `audioFile` 미지원, `isActive=false`와 cross-owner/invalid series의 DB/S3/event no-side-effect를 회귀한다. +- [ ] 수정 focused test와 `ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentUpdateTest` + +- [ ] **Task 3.8: 콘텐츠 ownership·오류·legacy 회귀 보완** + +**Goal 실행 `P3-T7`:** Phase 3 모든 endpoint의 ownership·ADMIN·오류 계약과 legacy/public 회귀를 고정한다. + +- **추적 review ID:** `REV-006`, `REV-007`, `REV-008`. +- **시작 조건:** `P3-T6` 완료 또는 앞선 Goal의 근거 있는 `해당 없음` 판정. +- **완료 증거:** 권한 매트릭스, 정확한 domain/client 오류 status/key/KO·EN·JA, no-side-effect와 legacy 회귀 기록. +- **범위 밖:** Phase 1 공통 handler 재설계, Phase 4 series 기능. + +**Files:** + +- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentControllerTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/AiCharacterAdminAudioContentOwnershipTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/AiCharacterAdminAuthorizationTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/AiCharacterAdminErrorContractTest.kt` + +- [ ] **RED:** 테마·목록·상세·생성·수정 endpoint 각각의 JWT role × DB role, stale ADMIN claim과 허용·거부 Origin을 parameterized test로 고정한다. +- [ ] **RED:** target/content/theme/series/date와 세 multipart part 누락의 exact status, exception type, message key와 KO/EN/JA envelope를 실제 endpoint에서 고정한다. +- [ ] **RED 확인:** 실제 endpoint matrix와 legacy characterization을 실행해 누락된 인가·i18n·failure-order assertion이 의도대로 실패하는지 확인한다. +- [ ] **GREEN:** 확정된 domain/client/server 오류만 최소 매핑하고 ownership 실패 시 DB insert/update/delete, S3, event 0건을 보장한다. +- [ ] **GREEN 확인:** 같은 endpoint/error/ownership test를 다시 실행해 status/header/envelope, KO/EN/JA와 no-side-effect가 모두 통과하는지 확인한다. +- [ ] **REFACTOR:** legacy characterization에 validation·파일·가격·공개/예약·번역/알림·failure order를 보강하고 signed URL edge case와 함께 실행한다. +- [ ] 실제 test 파일 목록과 targeted 명령을 대조해 존재하지 않는 `AiCharacterAdminAudioContentServiceTest`, `AiCharacterAdminAudioSignedUrlTest` 참조와 과거 test 수는 삭제하지 않고 정정 기록을 누적한다. +- [ ] `AiCharacterAdminAudioContentThemeControllerTest`, `AiCharacterAdminAudioContentQueryTest`, `AiCharacterAdminAudioContentCreateTest`, `AiCharacterAdminAudioContentUpdateTest`, `AiCharacterAdminAudioContentOwnershipTest`의 파일 존재와 각 소유 계약 통과를 확인한다. +- [ ] creator/admin/public content 회귀, 신규 DTO 의존 방향과 focused test·`ktlintCheck` 결과를 Progress에 기록한다. +- Verify: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*' --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminAuthorizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTest` + +#### Phase 3 Gate + +**Goal 실행 `P3-GATE`:** Phase 3 콘텐츠 관리와 signed URL의 PRD 추적성, pipeline 안전성과 legacy 회귀를 최종 판정한다. + +- [ ] **`P3-GATE` 완료:** 시작 조건과 완료 증거를 모두 충족하고 현재 상태표와 Progress를 갱신한다. + +- **시작 조건:** `P3-R1`, `P3-T3`~`P3-T7` 완료 또는 근거 있는 `해당 없음` 판정. +- **완료 증거:** 아래 명령 성공, review 후보 0건, 확정 finding 처리 완료와 Progress 기록. +- **범위 밖:** Gate 실패와 무관한 Phase 4 기능 구현. + +- [ ] `REV-004`~`REV-008`의 response parity matrix, multipart exception, series metadata, DTO 경계와 test 증거가 각 소유 Goal의 Progress에 연결됐다. +- [ ] 동일 `seriesIds`의 row metadata 보존, legacy `releaseDate`·rental 파생값, 세 필수 part와 실제 endpoint 권한·i18n matrix에 미결정 항목이 없다. +- [ ] 완료 이력의 누락 test 파일·test 수·characterization 범위는 원문을 삭제하지 않고 최신 정정 기록으로 재현 가능하게 남겼다. + +```bash +./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*' +./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminAuthorizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTest +./gradlew ktlintCheck +``` + +**Expected:** 모든 명령 exit code 0, Phase 3 review의 후보·확정 미처리 항목 0건, Phase 4가 소비할 owner query 계약 확정. --- @@ -1125,18 +1534,132 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 - `feat: add ai character admin series slice` - [ ] **Task 4.1: 기존 series parity 특성화 baseline 고정** - - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/LegacyCreatorAdminSeriesCharacterizationTest.kt` - - CHARACTERIZE: 기존 creator series 구현을 대상으로 목록/상세/생성/수정/soft delete, inactive 조회 정책, 콘텐츠 연결/해제, 조회/검색, 순서 변경의 결과·검증·side effect를 고정한다. - - BASELINE: 신규 v2 production code 변경 전에 특성화 테스트가 기존 구현을 대상으로 통과함을 확인한다. - - REFACTOR: fixture와 assertion naming만 정리하고 series parity baseline은 변경하지 않는다. - - Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.series.LegacyCreatorAdminSeriesCharacterizationTest` -- [ ] **Task 4.2: series controller/facade/DTO 구현** - - Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/*` - - RED: Task 4.1 baseline에서 옮긴 series parity, cross-owner detail/update/delete/order, cross-owner content attach, 시리즈 콘텐츠 검색, pagination 경계값 테스트와 controller 권한 테스트를 작성하고 신규 v2 미구현으로 실패함을 확인한다. - - GREEN: owner-scoped series endpoint와 시리즈 콘텐츠 `search/page/size` 조회를 구현한다. - - REFACTOR: 기존 owner-less order update를 참조하지 않는지 확인하고 회귀 테스트를 재실행한다. - - Verify: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.series.*'` +**Goal 실행 `P4-T1`:** 기존 creator series의 CRUD·연결·검색·순서 동작을 신규 v2 구현의 비교 기준으로 고정한다. + +- **시작 조건:** `P3-GATE` 완료. +- **완료 증거:** production 변경 전 특성화 테스트 통과, 관찰된 오류·side-effect 정책과 Progress 기록. +- **범위 밖:** 신규 v2 series production code 구현. + +**Files:** + +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/LegacyCreatorAdminSeriesCharacterizationTest.kt` + +- [ ] 목록·상세·생성·수정·soft delete와 inactive 조회 baseline test를 작성한다. +- [ ] 콘텐츠 연결·해제·검색과 순서 변경의 결과·검증·side effect를 고정한다. +- [ ] Phase 4 domain/client 오류별 정확한 status와 KO/EN/JA message key를 확정해 구현 Goal에 반영한다. +- [ ] production code 변경 없이 특성화 테스트가 기존 구현을 대상으로 통과함을 확인한다. +- [ ] fixture만 정리하고 test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 4.2: 시리즈 목록·상세 조회 구현** + +**Goal 실행 `P4-T2`:** target owner의 활성 시리즈 목록·상세를 pagination 계약으로 제공한다. + +- **시작 조건:** `P4-T1` 완료와 Phase 4 오류 계약의 계획 반영. +- **완료 증거:** 목록·상세·inactive·cross-owner·pagination RED/GREEN과 Progress 기록. +- **범위 밖:** 시리즈 mutation, 콘텐츠 연결, 순서 변경. + +**Files:** + +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesController.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesDto.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesFacade.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesQueryTest.kt` + +- [ ] 미구현 목록·상세, inactive 제외, cross-owner 거부와 pagination 경계 실패 test를 작성한다. +- [ ] owner-scoped 목록·상세 최소 구현으로 focused test를 통과시킨다. +- [ ] `page` 기본 0, `size` 기본·최소 20·최대 50과 legacy DTO 비노출을 검증한다. +- [ ] focused test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 4.3: 시리즈 생성·수정·soft delete 구현** + +**Goal 실행 `P4-T3`:** target owner의 시리즈 생성·수정·soft delete를 기존 creator parity로 제공한다. + +- **시작 조건:** `P4-T1`, `P4-T2` 완료. +- **완료 증거:** CRUD RED/GREEN, ownership·inactive·no-side-effect와 legacy 회귀 기록. +- **범위 밖:** 콘텐츠 연결·해제, 순서 변경, hard delete. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesController.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesDto.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesFacade.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesMutationTest.kt` + +- [ ] 생성·수정·soft delete와 cross-owner mutation 실패 test를 작성한다. +- [ ] owner 검증 후 최소 CRUD 구현으로 test를 통과시킨다. +- [ ] `isActive=false`와 활성 조회 제외, invalid target의 DB/event no-side-effect를 검증한다. +- [ ] focused/legacy test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 4.4: 시리즈 콘텐츠 조회·검색·연결·해제 구현** + +**Goal 실행 `P4-T4`:** 동일 owner의 시리즈와 콘텐츠만 검색·연결·해제할 수 있도록 한다. + +- **시작 조건:** `P4-T2`, `P4-T3`와 Phase 3 owner query 계약 완료. +- **완료 증거:** 검색·pagination·전체 ID 사전 검증·원자적 연결/해제 RED/GREEN과 Progress 기록. +- **범위 밖:** 콘텐츠 자체 수정, 시리즈 순서 변경. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesController.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesContentTest.kt` + +- [ ] 콘텐츠 조회·검색·연결·해제와 cross-owner ID 실패 test를 작성한다. +- [ ] 모든 series/content ID를 mutation 전에 검증하는 최소 구현을 통과시킨다. +- [ ] 일부 연결 성공이 남지 않는 원자성과 pagination 경계를 검증한다. +- [ ] focused/Phase 3 owner query 회귀와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 4.5: owner-scoped 시리즈 순서 변경 구현** + +**Goal 실행 `P4-T5`:** 요청된 모든 series ID의 owner를 먼저 검증한 뒤 한 transaction에서 순서를 변경한다. + +- **시작 조건:** `P4-T3` 완료. +- **완료 증거:** 정상·중복/누락·cross-owner·동시 요청 RED/GREEN과 owner-less 경로 비사용 증거. +- **범위 밖:** 기존 `CreatorAdminContentSeriesController.updateSeriesOrders(ids)` 변경. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesController.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesOrderTest.kt` + +- [ ] 정상 순서와 cross-owner ID-only 취약 경로를 재현하는 실패 test를 작성한다. +- [ ] 동일 owner 전체 검증 후 한 transaction에서 갱신하는 최소 구현을 통과시킨다. +- [ ] 검증 실패 시 update 0건과 동시 요청의 기존 last-transaction 정책을 확인한다. +- [ ] focused/legacy order test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 4.6: Phase 4 보안·오류·회귀 검증** + +**Goal 실행 `P4-T6`:** 모든 series endpoint의 ADMIN·오류·ownership 계약과 legacy 회귀를 고정한다. + +- **시작 조건:** `P4-T2`~`P4-T5` 완료. +- **완료 증거:** endpoint 권한 매트릭스, domain 오류 status/key/KO·EN·JA, legacy 회귀와 Progress 기록. +- **범위 밖:** Phase 5 community 기능. + +**Files:** + +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/series/AiCharacterAdminSeriesContractTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/AiCharacterAdminAuthorizationTest.kt` + +- [ ] endpoint별 ADMIN 이중 인가와 stale claim을 검증한다. +- [ ] target/series/content/pagination 오류의 정확한 status, message key, KO/EN/JA envelope를 확정·검증한다. +- [ ] invalid ownership의 DB/event side effect 0건과 legacy creator series 계약을 검증한다. +- [ ] Phase 4 focused test와 `ktlintCheck` 결과를 Progress에 기록한다. + +#### Phase 4 Gate + +**Goal 실행 `P4-GATE`:** Phase 4 series 사용자 흐름과 ownership·회귀 품질을 최종 판정한다. + +- [ ] **`P4-GATE` 완료:** 시작 조건과 완료 증거를 모두 충족하고 현재 상태표와 Progress를 갱신한다. + +- **시작 조건:** `P4-T1`~`P4-T6` 완료. +- **완료 증거:** `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.series.*'`와 + `./gradlew ktlintCheck` 성공, Progress 기록. +- **범위 밖:** 실패와 무관한 Phase 5 구현. --- @@ -1198,18 +1721,131 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 - `feat: add ai character admin community slice` - [ ] **Task 5.1: 기존 community behavior 특성화 baseline 고정** - - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/LegacyCommunityPostCharacterizationTest.kt` - - CHARACTERIZE: 기존 community 구현을 대상으로 max fixed count, image/audio/paid post validation, notification/recent-news side effect, fixed post soft delete clearing의 결과와 실패 계약을 고정한다. - - BASELINE: 신규 v2 production code 변경 전에 특성화 테스트가 기존 구현을 대상으로 통과함을 확인한다. - - REFACTOR: media fixture와 event spy 중복만 정리하고 side-effect parity baseline은 변경하지 않는다. - - Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.community.LegacyCommunityPostCharacterizationTest` -- [ ] **Task 5.2: community controller/facade/DTO 구현** - - Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/*` - - RED: Task 5.1 baseline에서 옮긴 community parity, cross-owner update, invalid target no-side-effect 테스트와 controller 권한/페이지네이션 경계 테스트를 작성하고 신규 v2 미구현으로 실패함을 확인한다. - - GREEN: owner-scoped community endpoint를 구현하고 목록 `page/size` 기본값·최소·최대 보정을 적용한다. - - REFACTOR: public community 조회 DTO/로직을 무비판적으로 복제하지 않았는지 확인하고 회귀 테스트를 재실행한다. - - Verify: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.community.*'` +**Goal 실행 `P5-T1`:** 기존 community의 media·유료·고정·soft delete·알림 동작을 신규 v2 비교 기준으로 고정한다. + +- **시작 조건:** `P4-GATE` 완료. +- **완료 증거:** production 변경 전 특성화 테스트 통과, 오류·side-effect·동시성 관찰 결과와 Progress 기록. +- **범위 밖:** 신규 v2 community production code 구현. + +**Files:** + +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/LegacyCommunityPostCharacterizationTest.kt` + +- [ ] image/audio/paid post validation과 notification/recent-news side effect baseline을 작성한다. +- [ ] 최대 고정 3개와 fixed post soft delete clearing baseline을 작성한다. +- [ ] Phase 5 domain/client 오류별 정확한 status와 KO/EN/JA message key를 확정해 구현 Goal에 반영한다. +- [ ] production code 변경 없이 기존 구현 대상 특성화 테스트 통과를 확인한다. +- [ ] fixture/event spy만 정리하고 test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 5.2: 관리자 게시글 목록 조회 구현** + +**Goal 실행 `P5-T2`:** target owner의 관리자용 커뮤니티 게시글 목록을 안전한 전용 DTO와 pagination으로 제공한다. + +- **시작 조건:** `P5-T1` 완료와 Phase 5 오류 계약의 계획 반영. +- **완료 증거:** owner/inactive/pagination/DTO RED/GREEN과 public 조회 무비판적 복제 없음 증거. +- **범위 밖:** 게시글 생성·수정·고정·삭제. + +**Files:** + +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostController.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostDto.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostFacade.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostQueryTest.kt` + +- [ ] 미구현 목록, owner 격리, pagination 경계와 관리자 DTO 실패 test를 작성한다. +- [ ] 최소 owner-scoped query와 `page/size` 보정으로 focused test를 통과시킨다. +- [ ] 유료/media private 정보와 public viewer 상태를 부적절하게 노출하지 않는지 검증한다. +- [ ] focused test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 5.3: 커뮤니티 게시글 생성 구현** + +**Goal 실행 `P5-T3`:** target creatorMember 작성자로 이미지·오디오·유료 게시글을 기존 검증과 side effect parity로 생성한다. + +- **시작 조건:** `P5-T1`, `P5-T2` 완료. +- **완료 증거:** 정상/media/paid validation RED/GREEN, writer/owner와 S3/event 결과, Progress 기록. +- **범위 밖:** 게시글 수정·고정·soft delete, 구매 기능. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostController.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostDto.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostFacade.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostCreateTest.kt` + +- [ ] 정상/image/audio/paid validation과 invalid target 실패 test를 작성한다. +- [ ] 해석된 creatorMember를 writer/owner로 사용하는 최소 생성 구현을 통과시킨다. +- [ ] S3, notification, recent-news 호출 순서와 실패 시 부분 저장/no-side-effect를 검증한다. +- [ ] focused/legacy characterization test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 5.4: 게시글 수정·고정·soft delete 구현** + +**Goal 실행 `P5-T4`:** owner 게시글만 수정·고정/해제하고 soft delete 시 고정 상태와 시간을 함께 제거한다. + +- **시작 조건:** `P5-T1`, `P5-T2` 완료. +- **완료 증거:** update/fixed/soft delete/cross-owner RED/GREEN과 transaction 결과 기록. +- **범위 밖:** hard delete, 구매·좋아요·댓글 관리. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostController.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostUpdateTest.kt` + +- [ ] 수정·고정/해제·soft delete와 cross-owner 실패 test를 작성한다. +- [ ] owner 검증 후 최소 mutation 구현으로 test를 통과시킨다. +- [ ] soft delete가 한 transaction에서 `isActive=false`, `isFixed=false`, `fixedAt=null`을 적용하는지 검증한다. +- [ ] focused/legacy characterization test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 5.5: 최대 고정 수·동시성·side effect 검증** + +**Goal 실행 `P5-T5`:** 최대 고정 게시글 3개 정책이 동시 요청과 실패에서도 깨지지 않도록 고정한다. + +- **시작 조건:** `P5-T4` 완료. +- **완료 증거:** 세 번째/네 번째 고정, 동시 요청, cross-owner와 DB/S3/event side effect RED/GREEN 기록. +- **범위 밖:** 새로운 lock/dependency 도입, 기존 고정 정책 변경. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostRepository.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostFacade.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostConcurrencyTest.kt` + +- [ ] 3개 허용·4번째 거부와 동시 고정 요청 실패 test를 작성한다. +- [ ] 기존 repository count/update 순서를 유지하는 최소 구현으로 test를 통과시킨다. +- [ ] invalid target/ownership 실패 시 DB/S3/event 0건을 검증한다. +- [ ] concurrency/focused test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 5.6: Phase 5 보안·오류·회귀 검증** + +**Goal 실행 `P5-T6`:** 모든 community endpoint의 ADMIN·오류·ownership 계약과 기존 public/legacy 회귀를 고정한다. + +- **시작 조건:** `P5-T2`~`P5-T5` 완료. +- **완료 증거:** endpoint 권한 매트릭스, 오류 status/key/KO·EN·JA, legacy/public 회귀와 Progress 기록. +- **범위 밖:** Phase 6 FanTalk 기능. + +**Files:** + +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/community/AiCharacterAdminCommunityPostContractTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/AiCharacterAdminAuthorizationTest.kt` + +- [ ] endpoint별 ADMIN 이중 인가와 stale claim을 검증한다. +- [ ] target/post/media/fixed-count 오류의 정확한 status, message key, KO/EN/JA envelope를 확정·검증한다. +- [ ] HUMAN/cross-character 게시글 mutation 거부와 legacy/public community 계약을 검증한다. +- [ ] Phase 5 focused test와 `ktlintCheck` 결과를 Progress에 기록한다. + +#### Phase 5 Gate + +**Goal 실행 `P5-GATE`:** Phase 5 community 사용자 흐름과 고정·side-effect·회귀 품질을 최종 판정한다. + +- [ ] **`P5-GATE` 완료:** 시작 조건과 완료 증거를 모두 충족하고 현재 상태표와 Progress를 갱신한다. + +- **시작 조건:** `P5-T1`~`P5-T6` 완료. +- **완료 증거:** `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.community.*'`와 + `./gradlew ktlintCheck` 성공, Progress 기록. +- **범위 밖:** 실패와 무관한 Phase 6 구현. --- @@ -1268,18 +1904,91 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 - `feat: add ai character admin fan talk reply slice` - [ ] **Task 6.1: 기존 FanTalk reply 의미 특성화 baseline 고정** - - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/LegacyFanTalkReplyCharacterizationTest.kt` - - CHARACTERIZE: 기존 FanTalk 구현을 대상으로 valid root reply의 언어 감지, 응답 DTO 의미, writer/creator 저장 결과를 고정한다. - - BASELINE: 신규 v2 production code 변경 전에 특성화 테스트가 기존 구현을 대상으로 통과함을 확인한다. - - REFACTOR: FanTalk root/reply fixture만 정리하고 언어 감지·응답·writer/creator baseline은 변경하지 않는다. - - Verify: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.fantalk.LegacyFanTalkReplyCharacterizationTest` -- [ ] **Task 6.2: FanTalk reply controller/facade/DTO 구현** - - Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/*` - - RED: Task 6.1 baseline에서 옮긴 FanTalk reply parity, cross-character/nested/inactive/missing reject, writer/creator 일관성, no-side-effect 테스트와 controller 권한 테스트를 작성하고 신규 v2 미구현으로 실패함을 확인한다. - - GREEN: root/active/owner 검증 후 reply 저장 endpoint를 구현한다. - - REFACTOR: nested reply 금지와 writer/creator mapping을 정리하고 회귀 테스트를 재실행한다. - - Verify: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.fantalk.*'` +**Goal 실행 `P6-T1`:** 기존 FanTalk의 root 판별, 언어 감지, 응답 의미와 writer/creator 저장 결과를 비교 기준으로 고정한다. + +- **시작 조건:** `P5-GATE` 완료. +- **완료 증거:** production 변경 전 특성화 테스트 통과, 중복 답변 정책과 Progress 기록. +- **범위 밖:** 신규 v2 reply production code 구현. + +**Files:** + +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/LegacyFanTalkReplyCharacterizationTest.kt` + +- [ ] valid root reply의 언어 감지, response 의미와 writer/creator 저장 baseline을 작성한다. +- [ ] root/nested/active 판별과 기존 중복 답변 정책을 관찰해 기록한다. +- [ ] Phase 6 domain/client 오류별 정확한 status와 KO/EN/JA message key를 확정해 구현 Goal에 반영한다. +- [ ] production code 변경 없이 기존 구현 대상 특성화 테스트 통과를 확인한다. +- [ ] fixture만 정리하고 test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 6.2: FanTalk root reply 저장 구현** + +**Goal 실행 `P6-T2`:** 선택한 AI 캐릭터의 활성 root FanTalk에 creator reply를 저장하고 전용 응답을 반환한다. + +- **시작 조건:** `P6-T1` 완료와 Phase 6 오류 계약의 계획 반영. +- **완료 증거:** 정상 저장·언어 감지·DTO·writer/creator RED/GREEN과 Progress 기록. +- **범위 밖:** FanTalk 원글, nested reply, 일반 사용자 대리 작성. + +**Files:** + +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyController.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyDto.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyFacade.kt` +- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyCreateTest.kt` + +- [ ] 미구현 정상 root reply, 언어 감지와 응답 DTO 실패 test를 작성한다. +- [ ] root 조회와 reply 저장을 같은 transaction에서 수행하는 최소 구현을 통과시킨다. +- [ ] writer/creator가 resolver의 creatorMember와 일치하고 principal impersonation이 없음을 검증한다. +- [ ] focused/legacy characterization test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 6.3: FanTalk target·root·ownership 거부 구현** + +**Goal 실행 `P6-T3`:** cross-character, nested, inactive, missing FanTalk를 저장·이벤트 없이 거부한다. + +- **시작 조건:** `P6-T1`, `P6-T2` 완료. +- **완료 증거:** 네 거부 분기의 정확한 오류 계약, DB/event 0건과 Progress 기록. +- **범위 밖:** 새로운 중복 답변 차단 정책. + +**Files:** + +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyFacade.kt` +- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyRepository.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyOwnershipTest.kt` + +- [ ] cross-character/nested/inactive/missing 각각의 실패 test와 의도한 실패를 확인한다. +- [ ] target·root·active·owner를 저장 전에 검증하는 최소 구현을 통과시킨다. +- [ ] 각 실패의 정확한 status/message key/KO·EN·JA와 reply insert/event 0건을 검증한다. +- [ ] focused test와 `ktlintCheck` 결과를 Progress에 기록한다. + +- [ ] **Task 6.4: Phase 6 보안·오류·회귀 검증** + +**Goal 실행 `P6-T4`:** FanTalk reply endpoint의 ADMIN·오류 계약과 기존 FanTalk 회귀를 고정한다. + +- **시작 조건:** `P6-T2`, `P6-T3` 완료. +- **완료 증거:** 권한 매트릭스, request binding/domain 오류, legacy 회귀와 Progress 기록. +- **범위 밖:** Phase 7 외 전체 기능 수정. + +**Files:** + +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/fantalk/AiCharacterAdminFanTalkReplyContractTest.kt` +- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/AiCharacterAdminAuthorizationTest.kt` + +- [ ] endpoint의 ADMIN 이중 인가와 stale claim을 검증한다. +- [ ] 빈 content/binding/domain 오류의 정확한 status, message key, KO/EN/JA envelope를 확정·검증한다. +- [ ] 기존 FanTalk 조회·작성 계약과 AI 로그인/token/impersonation 부재를 확인한다. +- [ ] Phase 6 focused test와 `ktlintCheck` 결과를 Progress에 기록한다. + +#### Phase 6 Gate + +**Goal 실행 `P6-GATE`:** Phase 6 FanTalk reply의 root·ownership·저장·회귀 품질을 최종 판정한다. + +- [ ] **`P6-GATE` 완료:** 시작 조건과 완료 증거를 모두 충족하고 현재 상태표와 Progress를 갱신한다. + +- **시작 조건:** `P6-T1`~`P6-T4` 완료. +- **완료 증거:** `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.fantalk.*'`와 + `./gradlew ktlintCheck` 성공, Progress 기록. +- **범위 밖:** 실패와 무관한 신규 기능. --- @@ -1320,7 +2029,8 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 - 모든 신규 endpoint에서 stale ADMIN claim은 403이고, 등록된 모든 API 오류 분기는 정확한 비2xx status + `ApiResponse.error` + KO/EN/JA message를 반환한다. Spring CORS 정책 거부 403 body는 envelope 계약의 예외다. - legacy/public 401/403/domain 오류의 status/body/message가 특성화 baseline과 동일하다. -- `./gradlew test`가 통과한다. +- 전체 회귀 필요성 판정 결과 실행 대상이면 `./gradlew test`가 통과하고, 생략 대상이면 근거와 대체 targeted/영향 범위 회귀 + 결과가 기록된다. - Kotlin 파일 변경이 있으면 `./gradlew ktlintCheck`가 통과한다. - 신규 dependency와 신규 DDL이 없다. - source spec acceptance criteria 25개를 각 Phase 결과와 대조해 누락이 없다. @@ -1329,10 +2039,12 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 - Run: ```bash ./gradlew test --tests kr.co.vividnext.sodalive.jwt.TokenProviderTest --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.*' - ./gradlew test ./gradlew ktlintCheck ``` +- Conditional full regression: 공통 경계·여러 Phase 영향 또는 targeted 결과로 영향 범위를 판단할 수 없을 때만 + `./gradlew test`를 추가 실행한다. + #### 전체 회귀 테스트 영향 - legacy `/admin/*`, `/creator-admin/*`, public `/api/v2/*` 관련 테스트 전체가 회귀 범위다. - 기존 성공·오류 request/response 계약 변경이 없음을 controller/DTO diff와 legacy 오류 특성화 테스트로 확인한다. @@ -1344,25 +2056,138 @@ AI 캐릭터 목록/검색/상세/생성/수정/비활성화를 신규 ADMIN v2 #### 권장 commit 경계 - `test: verify ai character admin api integration` -- [ ] **Task 7.1: 전체 targeted/regression test 실행** +- [ ] **Task 7.1: 전체 targeted 및 필요 시 전체 회귀 test 실행** + - **Goal 실행 `P7-T1`:** Phase 1~6 targeted test를 실행하고 위험 근거에 따라 전체 회귀 필요성을 판정해 결과를 확정한다. + - **시작 조건:** `P2-GATE`~`P6-GATE` 완료와 Phase 1 완료 증거 확인. + - **완료 증거:** targeted 결과와 전체 회귀 실행 또는 생략 판정·근거를 Progress와 하단 검증 기록에 누적. + - **범위 밖:** 실패와 무관한 기능 추가·리팩터링. - TDD 예외 사유: 구현 완료 후 검증 전용 Task라 신규 실패 테스트를 작성하지 않는다. - 대체 검증 방법: Phase 1~6 targeted test, `AiCharacterAdminAuthorizationTest`, `AiCharacterAdminErrorContractTest`, 기존 admin - security test와 전체 회귀 테스트를 실행한다. + security test를 먼저 실행한다. 공통 경계·여러 Phase 영향 또는 targeted 실패로 영향 범위를 판단할 수 없을 때만 전체 회귀를 실행한다. - REFACTOR: 실패가 있으면 관련 Phase Task로 되돌려 최소 수정 후 다시 실행한다. - - Verify: 위 targeted command와 `./gradlew test`를 실행하고 결과를 이 문서 하단 검증 기록에 남긴다. + - Verify: 위 targeted command를 실행하고 전체 회귀 필요성을 판정한다. 실행 시 `./gradlew test` 결과를, 생략 시 근거와 대체 + 회귀 범위를 이 문서 하단 검증 기록에 남긴다. + - [ ] Phase 1~6 targeted test를 실행하고 각 결과를 기록한다. + - [ ] 공통 경계·여러 Phase 변경과 targeted 결과를 근거로 전체 회귀 필요성을 판정한다. + - [ ] 필요하면 `./gradlew test`의 exit code·실패 수를 기록하고, 불필요하면 생략 근거와 대체 회귀 범위를 기록한다. + - [ ] 실패가 있으면 소유 Phase에 별도 회귀 수정 Goal을 추가하고 `P7-T1`을 완료 처리하지 않는다. - [ ] **Task 7.2: API contract와 변경 범위 점검** + - **Goal 실행 `P7-T2`:** API·architecture·dependency·DDL·diff와 문서 추적성을 read-only로 최종 점검한다. + - **시작 조건:** `P7-T1` 완료. + - **완료 증거:** 아래 점검 체크박스, `ktlintCheck`, source spec acceptance criteria 추적 결과와 Progress 기록. + - **범위 밖:** 신규 기능 추가, 확정되지 않은 계약 보정. - TDD 예외 사유: diff/architecture 검증 전용 Task라 신규 실패 테스트를 작성하지 않는다. - 대체 검증 방법: legacy/public 성공·오류 status/body/message 특성화 baseline 통과, controller/DTO schema 변경 없음, 신규 dependency 없음, 신규 DDL 없음, 신규 v2 application/domain에서 기존 controller와 v2 response DTO 역참조 없음. - REFACTOR: 불필요한 import, 역방향 의존, 관련 없는 변경을 제거하고 diff를 다시 확인한다. - Verify: `git diff --name-only`, `./gradlew ktlintCheck` + - [ ] `git diff --name-only`와 `git diff --check`로 변경 범위와 문서/코드 오류를 확인한다. + - [ ] `build.gradle.kts`와 migration/DDL 경로를 확인해 신규 dependency·DDL 0건을 기록한다. + - [ ] legacy/public controller·DTO 외부 계약 diff와 신규 application/domain의 역방향 import 0건을 확인한다. + - [ ] source spec acceptance criteria 25개를 Phase Goal/Gate 완료 증거에 대조한다. + - [ ] `./gradlew ktlintCheck`와 `./gradlew tasks --all` 결과를 기록한다. + +#### Phase 7 Gate + +**Goal 실행 `P7-GATE`:** 모든 Phase의 완료 증거와 최신 전체 검증을 대조해 AI 캐릭터 관리자 API의 최종 완료 여부를 판정한다. + +- [ ] **`P7-GATE` 완료:** 시작 조건과 완료 증거를 모두 충족하고 문서 상태를 `구현 완료`로 갱신한다. + +- **시작 조건:** `P7-T1`, `P7-T2` 완료. +- **완료 증거:** 미완료 Goal·미처리 review finding·보류 없는 차단 사항 0건, 아래 완료 조건과 최종 Progress 기록. +- **범위 밖:** Gate에서 직접 production code 수정, test 삭제·skip·완화. + +- [ ] Phase 1~6의 Task/Gate 완료 증거와 하단 검증 기록이 일치한다. +- [ ] 모든 확정 review finding이 수정 완료 또는 근거 있는 제외로 종결됐다. +- [ ] 최신 targeted·ktlint·문서 명령이 성공했고, 전체 회귀는 필요성 판정에 따라 성공 결과 또는 생략 근거가 기록됐다. +- [ ] 남은 항목과 최종 상태를 Progress 및 최종 보고 형식으로 기록한다. --- +## 실행 순서와 의존성 + +| 순서 | Goal | 선행조건 | 병행 가능 | 차단 시 다음 행동 | +|---:|---|---|---|---| +| 1 | `P2-R1` | `P2-H1`, `P2-H2` | 아니요 | 리뷰 후보를 판정하고 계약 미확정이면 Decision Log에 기록 | +| 2 | `P2-T3` → `P2-T4` → `P2-T5` → `P2-T6` | `P2-R1` | 아니요 | 실패 소유 Goal에서 수정·검증을 끝낸 뒤 다음 Goal 수행 | +| 3 | `P2-GATE` | Phase 2 활성 Goal 전체 | 아니요 | 실패 소유 Task의 회귀 수정 Goal 추가 | +| 4 | `P3-R1` | `P2-GATE`, `P3-H1`, `P3-H2` | 아니요 | 리뷰 후보를 판정하고 계약 미확정이면 Decision Log에 기록 | +| 5 | `P3-T3` → `P3-T4` → `P3-T5` → `P3-T6` → `P3-T7` | `P3-R1` | 아니요 | 실패 소유 Goal에서 수정·검증을 끝낸 뒤 다음 Goal 수행 | +| 6 | `P3-GATE` | Phase 3 활성 Goal 전체 | 아니요 | 실패 소유 Task의 회귀 수정 Goal 추가 | +| 7 | `P4-T1`~`P4-T6` → `P4-GATE` | `P3-GATE` | 아니요 | 실패 소유 Task로 되돌림 | +| 8 | `P5-T1`~`P5-T6` → `P5-GATE` | `P4-GATE` | 아니요 | 실패 소유 Task로 되돌림 | +| 9 | `P6-T1`~`P6-T4` → `P6-GATE` | `P5-GATE` | 아니요 | 실패 소유 Task로 되돌림 | +| 10 | `P7-T1` → `P7-T2` → `P7-GATE` | Phase 1~6 Gate | 아니요 | 실패 소유 Phase에 회귀 수정 Goal 추가 | + +## 변경 금지·중단 규칙 + +- PRD와 Endpoint Contract Summary를 근거 없이 변경하거나 제공되지 않은 DTO·enum·오류 status/key를 추정하지 않는다. +- 기존 완료 체크박스, 검증 기록과 Progress를 삭제·되돌리거나 덮어쓰지 않는다. +- review 후보는 재현·판정 전 production 수정으로 전환하지 않고, 확정 항목만 관련 Goal 또는 신규 회귀 수정 Goal로 처리한다. +- test를 삭제·skip·완화하거나 관련 없는 refactor·dependency·DDL을 추가해 Gate를 통과시키지 않는다. +- JWT, token, signed URL 전체, private path와 파일 본문을 문서·fixture·log에 기록하지 않는다. +- 일부 체크박스, 일부 test 또는 코드 작성만 끝난 상태에서는 Goal을 `complete`로 갱신하지 않는다. + +## Goal Progress + +기존 기록을 삭제하거나 덮어쓰지 않고 Goal 실행 결과를 차수별로 누적한다. + +### `P2-R1` 실행 준비 — 2026-07-27 + +- 상태: 대기 +- 무엇을: Phase 2~7의 Goal ID, 시작 조건, 완료 증거, 범위 밖, 체크박스와 Gate 구조를 준비했다. +- 왜: 기존 Phase 2·3 체크만으로 심층 리뷰와 최종 완료를 판정할 수 없었기 때문이다. +- 어떻게: PRD, 기존 계획과 `docs/sample/sample-plan-task.md`, `docs/sample/sample-review.md`를 대조했다. +- 남은 항목: `P2-R1` read-only 리뷰 실행. +- 다음 행동: 사용자가 goal 실행을 요청하면 `P2-R1`만 `create_goal`에 등록한다. + +## Decision Log + +| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 | +|---|---|---|---|---|---| +| 2026-07-27 | `DEC-GOAL-001` | 확정 | 기존 Phase 2·3 완료 Task는 이력으로 보존하고 심층 리뷰·세부 보완·Gate Goal을 추가한다. | 기존 검증 이후 후속 보완이 반복됐고 Phase 완료 Gate가 없었다. | `P2-R1`~`P3-GATE` | +| 2026-07-27 | `DEC-REVIEW-001` | 확정 | 코드 리뷰의 8개 확정 finding은 기존 미실행 범주형 Goal에 `REV-001`~`REV-008`로 귀속하고 Phase 안에서 직렬 실행한다. | 새 Goal을 중복 추가하거나 기존 완료 이력을 다시 열지 않으면서 각 finding의 재현·완료 증거를 독립 추적하기 위해. | `P2-R1`~`P2-GATE`, `P3-R1`~`P3-GATE` | + +## 발견된 문제 + +| ID | 심각도 | 상태 | 발견 내용 | 영향 Goal | 처리 계획 | +|---|---|---|---|---|---| +| `REV-001` | Blocker | 확정 | 문자열 미매핑 경로가 `/{characterId}`에 매핑되어 Phase 1의 404 계약 대신 type mismatch 400을 반환한다. | `P2-R1`, `P2-T6` | numeric path mapping RED/GREEN과 KO/EN/JA·CORS 회귀 4건을 고정한다. | +| `REV-002` | High | 확정 | 캐릭터 생성·수정 DTO의 `systemPrompt`, `externalCharacterId`, 생성 `isActive`와 soft-delete 혼합 의미가 Endpoint Contract Summary와 다르다. | `P2-R1`, `P2-T4`, `P2-T5` | canonical request를 먼저 확정하고 문서 JSON 역직렬화·반영 test로 맞춘다. | +| `REV-003` | High | 확정 | 외부 API·S3·DB 실패 사이에 보상 경계가 없고 동시 중복 이름 생성의 원자성 증거가 없다. | `P2-R1`, `P2-T4`, `P2-T5` | failure-order characterization, 선검증, 동시성 정책과 보상/정리 결과를 고정한다. | +| `REV-004` | High | 확정 | 동일한 `seriesIds` 수정도 기존 연결을 삭제·재생성해 row ID·`orders`·`createdAt`을 소실한다. | `P3-R1`, `P3-T6` | 교집합 row를 보존하고 차집합만 변경하는 RED/GREEN을 추가한다. | +| `REV-005` | High | 확정 | 콘텐츠 상세의 `releaseDate`, `isOnlyRental`, `purchaseOption`이 legacy 상세 파생 규칙과 다르다. | `P3-R1`, `P3-T4` | legacy 조합 matrix와 v2 `releaseDateUtc` 분리 mapping을 고정한다. | +| `REV-006` | High | 확정 | 콘텐츠 생성의 필수 `coverImage`·`audioFile`이 nullable binding이라 정확한 missing-part 오류 계약을 우회한다. | `P3-R1`, `P3-T5`, `P3-T7` | non-null binding과 세 part별 exact exception·KO/EN/JA endpoint test를 추가한다. | +| `REV-007` | Medium | 확정 | 목록 초과 field, pre-flush timestamp, `characterType`, legacy/public 중첩 DTO, `tags`·생성 `isActive`에서 추가 계약/parity 차이가 있다. | `P2-T3`~`P2-T6`, `P3-T3`~`P3-T5`, `P3-T7` | 각 소유 Goal에서 exact schema·field 의미·DTO 경계와 legacy parity를 분리 검증한다. | +| `REV-008` | High | 확정 | endpoint별 인가/i18n, failure-order characterization과 계획에 명시된 test 파일·검증 수의 완료 증거가 부족하다. | `P2-R1`, `P2-T6`, `P3-R1`, `P3-T5`, `P3-T7` | 실제 endpoint matrix와 characterization을 보강하고 기존 기록을 삭제하지 않은 채 정정 기록을 누적한다. | + ## 검증 기록 +- Phase 2·3 코드 리뷰 Task 보완(2026-07-27): 확정 finding 8개를 `REV-001`~`REV-008`로 등록하고 기존 미실행 + `P2-T3`~`P2-T6`, `P3-T3`~`P3-T7`에 소유권, 정확한 RED/RED 확인/GREEN/GREEN 확인/REFACTOR, 파일 경로, + focused 명령과 Gate 완료 증거를 보강했다. 중복 Goal은 만들지 않고 각 Phase 안에서 직렬 실행하도록 의존성 표를 갱신했다. +- Task TDD 샘플 동기화(2026-07-27): `docs/sample/sample-plan-task.md`에 `RED → RED 확인 → GREEN → GREEN 확인 → REFACTOR` + 작성 규칙과 read-only Task의 `TDD 예외 사유`·`대체 검증 방법` 형식을 추가하고 기존 샘플 Task 체크박스를 같은 형식으로 통일했다. +- 문서 보완 검증(2026-07-27): `REV-001`~`REV-008` 추적 횟수, Phase 2·3 Task/Gate·직렬 Goal 순서, 중복 Goal ID와 + 미교체 placeholder를 `rg`로 확인했고 `git diff --check`, `git diff --cached --check`가 통과했다. + `./gradlew tasks --all`은 exit code 0, `BUILD SUCCESSFUL in 660ms`를 확인했다. 문서 전용 변경이며 사용자가 명시적으로 + 제외했으므로 전체 `./gradlew test`는 실행하지 않았다. +- 리뷰 문서 경로 보완(2026-07-27): 여러 리뷰를 범위별 파일로 누적할 수 있도록 공통 저장 경로를 + `docs/[날짜]_구현할내용한글/reviews/`로 정하고, Phase 2·3 리뷰 산출물과 샘플·가이드 경로를 동기화했다. +- 전체 회귀 실행 정책 보완(2026-07-27): focused/영향 범위 회귀를 우선하고 공통 경계·여러 Phase 영향 또는 targeted 결과만으로 + 영향 범위를 판단할 수 없는 경우에만 전체 회귀를 실행하도록 공통 가이드·샘플·`P7-T1`을 동기화했다. 생략 시 근거와 대체 + 검증 명령을 기록하도록 했다. +- Goal 실행형 계획 보완(2026-07-27): `docs/sample/sample-plan-task.md`와 PRD Feature B~F를 대조해 Phase 2·3에는 기존 완료 + 이력을 보존한 review/세부 보완/Gate Goal을 추가하고, Phase 4~6은 독립 검토 가능한 기능 단위 Task와 Gate로 분할했다. + Phase 7에는 targeted·조건부 전체 회귀, 계약·변경 범위와 최종 판정 Goal을 분리했다. +- 샘플 참조 가이드 보완(2026-07-27): `docs/agent-guides/작업절차.md`, `docs/agent-guides/문서유지보수.md`에 + `docs/sample/{sample-prd.md,sample-plan-task.md,sample-review.md}`의 용도, Goal 완료·Gate·리뷰 누적 규칙을 반영했다. +- 문서 검증(2026-07-27): Goal/Task/Gate heading과 ID, placeholder·구 샘플 경로, 중복 Goal ID를 `rg`로 확인했고 중복·미교체 + placeholder·구 샘플 경로 참조가 없음을 확인했다. `git diff --check`가 통과했다. +- 명령 유효성 검증(2026-07-27): sandbox 실행은 Gradle wrapper lock 권한으로 실패해 승인 범위에서 + `./gradlew tasks --all`을 재실행했고 `BUILD SUCCESSFUL`을 확인했다. - 계획 작성 단계: 코드 변경 없음. -- 문서 규칙 확인: `docs/agent-guides/작업절차.md`, `docs/agent-guides/문서유지보수.md`, `docs/agent-guides/테스트스타일.md`, `docs/prd/sample-prd.md`를 확인하고 `docs/20260724_AI캐릭터_관리자_API/{prd.md,plan-task.md}` 형식으로 작성했다. +- 문서 규칙 확인: `docs/agent-guides/작업절차.md`, `docs/agent-guides/문서유지보수.md`, `docs/agent-guides/테스트스타일.md`, `docs/sample/sample-prd.md`를 확인하고 `docs/20260724_AI캐릭터_관리자_API/{prd.md,plan-task.md}` 형식으로 작성했다. - 금지어 확인: 계획 문서 금지어 검색 명령 실행 결과 없음. - Phase 항목 확인: `rg -n "^### Phase|#### 목표|#### 범위와 비범위|#### 선행 Phase|#### API endpoint|#### entity, repository, service 변경|#### DB migration|#### transaction과 concurrency|#### 보안 및 개인정보|#### acceptance criteria|#### targeted test|#### 전체 회귀 테스트 영향|#### rollback 전략|#### 권장 commit 경계|Final Integration" "docs/20260724_AI캐릭터_관리자_API/plan-task.md"`로 Phase 1~7 전체에 필수 항목이 있음을 확인했다. - 명령 유효성 확인: `./gradlew tasks --all` 실행 결과 `BUILD SUCCESSFUL`. diff --git a/docs/20260724_AI캐릭터_관리자_API/prd.md b/docs/20260724_AI캐릭터_관리자_API/prd.md index 2c3d321a..cc6754f1 100644 --- a/docs/20260724_AI캐릭터_관리자_API/prd.md +++ b/docs/20260724_AI캐릭터_관리자_API/prd.md @@ -95,6 +95,7 @@ #### Requirements - 캐릭터 소유 콘텐츠 목록/검색/상세 조회, 생성, 수정, 기존 삭제 동작에 따른 soft delete를 제공한다. +- 콘텐츠 생성 화면에서 사용할 활성 콘텐츠 테마(카테고리) 목록 조회를 제공한다. 기존 크리에이터 관리자 콘텐츠 등록 화면의 테마 조회와 같은 기능이며, 신규 v2 응답 필드는 `themeId`, `themeName`, `imageUrl`로 명확히 구분한다. - 기존 크리에이터 콘텐츠 관리의 검증, 파일 처리, content upload/processing pipeline, 가격, 공개/예약, 번역/알림 등 business behavior parity를 유지한다. - 관리자 화면 재생용 signed URL을 콘텐츠 목록/상세 응답에 제공한다. - signed URL은 공통 `AudioContentCloudFront`를 재사용하고 기존 크리에이터 관리자와 같은 만료 정책을 따른다. @@ -182,6 +183,9 @@ - 신규 prefix 전용 오류 처리는 legacy/public endpoint의 기존 성공·오류 응답에 적용하지 않는다. - page 기반 조회는 기존 v2 탭 API 관례를 따라 `page` 기본값 0, `size` 기본값 20, 최소 20, 최대 50 보정을 기본안으로 하며, 경계값 보정은 구현 task와 테스트에 포함한다. - multipart 생성/수정은 기존 admin/creator-admin 관례대로 파일 part와 `request` JSON string part를 사용한다. +- `GET /api/v2/admin/ai-characters/audio-content-themes`는 request body 없이 활성 콘텐츠 테마 목록을 반환한다. 성공 응답 `data`는 `[{ "themeId": 11, "themeName": "ASMR", "imageUrl": "https://cdn.example.com/audio-content-theme/asmr.png" }]` 형태이며, 기존 내부/legacy DTO의 `id`, `theme`, `image` 필드명을 외부 계약으로 노출하지 않는다. +- 오디오 콘텐츠 생성 `request` JSON에는 콘텐츠 테마 선택값인 `themeId`와 기존 `CreateAudioContentRequest`의 생성 필드 전체를 포함한다. v2는 `detail` 대신 `description`, `releaseDate` 대신 UTC ISO-8601 `releaseDateUtc`를 외부 계약으로 사용하고 legacy pipeline 호출 시 변환한다. +- 오디오 콘텐츠 목록 응답은 현 v2 관리자 목록 계약을 유지한다. 상세 응답은 기존 `GetAudioContentDetailResponse`의 필드 전체를 v2 상세 DTO에 포함하되, 구매·좋아요·핀·추천·댓글 목록처럼 viewer 상태가 필요한 필드는 관리자 상세에서 안전한 기본값을 반환한다. - request/response DTO는 신규 v2 AI character admin API 전용 DTO로 두고 legacy/public DTO를 외부 계약으로 재노출하지 않는다. ---