Files
2026-07-28 03:33:16 +09:00

116 KiB

AI 캐릭터 관리자 웹 Phase 2 코드 리뷰·QA 리포트

계약 이력 안내 (2026-07-28): 이 리뷰의 api-contract.md section reference는 2026-07-27 당시 사용한 계약을 가리키는 역사 기록이다. 현재 구현 기준 계약은 api-contract.openapi.json이며, 기존 판정과 section reference는 당시 검증 근거를 보존하기 위해 수정하지 않는다.

1. 리뷰 정보

항목 내용
리뷰 대상 plan-task.md Phase 2 / P2-T1P2-T3, P2-GATE, P2-R1P2-R16과 현재 working tree 구현
기준 commit 또는 working tree b4841ff579a271771dc787b7c91b7ca2d24f5e85 기준, 5차 재검증 시 tracked diff 37개 + untracked 1개 = working tree 38개 변경 항목
리뷰 일자 2026-07-27
리뷰어 Codex
기준 문서 docs/20260725_AI캐릭터관리자웹/prd.md, api-contract.md, plan-task.md
리뷰 기준 docs/agent-guide/review.md, docs/sample/sample-review.md
리뷰 상태 7차 독립 재검증 후 P2-R16 수정 완료
최종 결론 Phase 2 구현·review·plan 정합성 충족, Phase 3 진행 가능

리뷰 시작 시 Phase 2 구현은 모두 index에 staged된 상태였다. 기존 staged 변경은 수정하지 않았고, 이 리뷰에서 별도 review 문서를 만들고 확정 항목을 plan-task.md의 신규 회귀 Task로만 전환했다.

2026-07-27 2차 재검증에서는 P2-R1P2-R3 수정이 반영된 현재 working tree를 독립 검토했다. 애플리케이션 코드·test·설정·plan-task.md는 변경하지 않았고, 아래 REV-P2-006REV-P2-009와 실제 검증 결과만 이 문서에 누적했다.

2026-07-27 3차 재검증에서는 P2-R4P2-R7 수정 뒤의 문서 추적성, mobile menu의 mock banner·responsive inert 경계와 보호 route 오류 복구 UI를 다시 확인했다. REV-P2-010REV-P2-013을 확정하고 plan-task.mdP2-R8~P2-R10 회귀 Task로 전환했으며, 애플리케이션 코드·test·설정은 변경하지 않았다.

2026-07-27 P2-R10에서는 보호 route 오류 page에 fail-closed 수동 retry를 추가하고 App unit, server boundary E2E, P2 focused Gate, 전체 unit·정적 검사·build와 실제 브라우저 시각 QA를 완료했다. REV-P2-013을 수정 완료로 판정해 3차 재검증의 남은 항목을 닫았다.

2026-07-27 4차 재검증에서는 P2-R8P2-R10 반영 뒤 working tree 전체 집계, 보호 route pending UI와 npm script의 focused E2E 필터를 독립 확인했다. REV-P2-014REV-P2-016을 확정하고 plan-task.mdP2-R11~P2-R13 회귀 Task로 전환했으며, 애플리케이션 코드·test·설정은 변경하지 않았다.

2026-07-27 P2-R11P2-R13에서는 untracked 포함 working tree 추적성, 보호 route pending loading status와 mode별 focused E2E filter를 수정했다. REV-P2-014REV-P2-016을 수정 완료로 판정했다.

2026-07-27 5차 재검증에서는 P2-R11~P2-R13 구현과 최신 실행 결과를 다시 대조했다. 제품 동작과 mode 경계 회귀는 통과했지만 P2-R12의 test 구조·개수 기록이 실제와 다른 REV-P2-017 Low 1건을 확정하고 plan-task.mdP2-R14 회귀 Task로 전환했다. 애플리케이션 코드·test·설정은 변경하지 않았다.

2026-07-27 P2-R14에서는 P2-R12 완료 증거를 실제 App test 구조인 최초 pending 전용 test 1건 + 기존 404 retry test 보강 + 27 tests로 정정했다. 문서 검색·diff 검증과 App focused test를 완료해 REV-P2-017을 수정 완료로 판정했다.

2026-07-27 6차 재검증에서는 P2-R14 반영 뒤 구현·test·production·mode별 E2E와 review의 현재 상태를 다시 대조했다. 제품 동작과 mode 경계는 통과했지만 review §7이 이미 수정 완료된 P2-R8~P2-R13을 여전히 “아직 수정하지 않았다”고 표시하는 REV-P2-018 Low 1건을 확정하고 plan-task.mdP2-R15 회귀 Task로 전환했다. 이후 P2-R15에서 review §7의 현재 상태 문구를 수정 완료 상태로 정정했다. 애플리케이션 코드·test·설정은 변경하지 않았다.

2026-07-27 7차 재검증에서는 P2-R15 반영 뒤 Phase 2 상단 현재 상태와 하단 최신 Progress를 Task 본문·review 종료 판정과 대조했다. P2-R15는 수정 완료지만 plan의 현재 상태는 최초 추가 당시 설명에 머물고, 최신 Progress는 여전히 P2-R15 수정 필요 상태로 끝나는 REV-P2-019 Low 1건을 확정해 P2-R16으로 전환했다. 이후 P2-R16에서 Phase 2 현재 상태와 최신 Progress를 완료 상태로 정렬하고 전체 Gate를 재검증했다. 애플리케이션 코드·test·설정은 변경하지 않았다.

2. 리뷰 목적과 범위

2.1 목적

  • Phase 2 완료 체크와 Gate 기록이 PRD MOCK-001~009, API Contract §1·§3, 실제 코드·test 결과와 일치하는지 확인한다.
  • mock/server 명시적 경계, production 차단, no-auto-fallback, auth fixture와 지속 안내가 실제 브라우저에서도 유지되는지 확인한다.
  • 자동화가 통과하더라도 보호 route, exact API origin, JWT 오류 status와 문서 추적성에 남은 공백을 독립적으로 판정한다.

2.2 포함 범위

  • 코드·설정: .env.example, package.json, playwright.config.ts, vite.config.ts, src/main.tsx, src/app, src/shared/config, src/shared/mocks, src/shared/ui/mock-mode-banner.tsx
  • 테스트: Phase 2 unit/contract test, mock/server boundary E2E, 기존 auth·accessibility·smoke 회귀 E2E
  • 문서: README, agent environment/scripts 가이드, PRD MOCK-001~009, API Contract §1·§3, plan P2-T1~P2-GATE
  • 런타임 검증: Chromium, WebKit, Mobile Chrome, Mobile Safari의 mock login/shell, 320px·200% zoom, axe, worker 등록, server 404·network error

2.3 제외 범위

  • Phase 3 이후 Character·Audio·Series·Community·FanTalk·Comments 구현
  • 실제 백엔드 내부 구현과 운영 데이터
  • 계약 미제공 endpoint·DTO·오류의 추정
  • 확정 문제의 코드·test 수정
  • 실제 보조기기 수동 테스트와 디자인 정성 평가

3. 판정 기준

3.1 심각도

심각도 기준
Blocker Phase 완료 판정이나 출고 판단을 무효화하거나 핵심 보호 경계를 전혀 신뢰할 수 없게 만드는 문제
High 확정 요구사항·API Contract·보호 route를 위반하거나 mock 검증이 잘못된 production 경계를 통과시키는 문제
Medium 제한된 조건에서 발생하는 기능·접근성·복구 문제
Low 사용자 안내, 문서 정합성 또는 검증 추적성을 약화하는 문제

3.2 상태

상태 의미 후속 처리
후보 근거를 발견했지만 아직 재현·판정하지 않음 검증 후 상태 변경
확정 코드·test·문서 또는 실행으로 문제가 확인됨 회귀 수정 Task·goal 후보
오탐 요구사항이나 실행 결과상 문제가 아님 근거를 남기고 종료
보류 외부 계약·환경·제품 결정이 필요함 담당 주체와 재개 조건 기록
수정 완료 수정과 관련 검증이 완료됨 실제 명령과 결과 연결

4. 검토한 근거

4.1 문서와 코드

  • 요구사항: PRD AUTH-003, MOCK-001~009, 성공 기준의 mock/server/production 항목
  • API Contract: §1.2 오류 status, §1.5 Mock Preview 계약 경계, §3.1~3.3 인증
  • 계획: P2-T1, P2-T2, P2-T3, P2-GATE, Phase 1 P1-R3
  • 코드: src/main.tsx, src/app/App.tsx, src/app/admin-pages.tsx, src/shared/config/env.ts, src/shared/mocks, vite.config.ts, playwright.config.ts
  • 테스트: src/app/App.test.tsx, src/shared/mocks, tests/e2e/mock-mode-boundary.spec.ts, mock-preview-shell.spec.ts, server-mode-boundary.spec.ts, 기존 auth/accessibility/smoke spec

4.2 실행 환경

항목
OS macOS 26.0, Darwin 25.0.0 x86_64
Node.js v24.12.0
npm 11.7.0
Playwright 1.61.1
브라우저 프로젝트 Chromium, WebKit, Mobile Chrome, Mobile Safari
server mode VITE_API_MODE=server, http://127.0.0.1:8888
mock mode VITE_API_MODE=mock, http://127.0.0.1:8889

민감정보는 실행 환경과 기록에 포함하지 않았다.

4.3 실행한 자동 검증

명령 종료 결과와 핵심 증거
npm run test:run 0 34 files / 130 tests 통과
npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx 0 Phase 2 focused 7 files / 20 tests 통과
npm run typecheck 0 TypeScript 오류 0건
npm run lint 0 ESLint 오류 0건
npm run build:dev 0 159 modules, JS 294.26 kB, gzip 88.61 kB
npm run build:prod 0 159 modules, JS 294.25 kB, gzip 88.60 kB
VITE_API_MODE=mock npm run build:prod 1 기대한 production 거부. VITE_API_MODE=mock is only available during development 확인
test ! -e dist/mockServiceWorker.js && ! rg -l -e 'startMockWorker' -e 'mockServiceWorker\.js' dist 0 production 산출물에 worker 파일·bootstrap 문자열 없음
npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts 1 최초 sandbox에서 127.0.0.1:8889 listen EPERM, 실행 불가 사유 확인
같은 mock preview E2E를 로컬 listen 권한으로 재실행 0 4 projects / 20 tests 통과. login, logout 후 재login, 320px·200% zoom, axe critical·serious 0건
npm run e2e:mock -- tests/e2e/mock-mode-boundary.spec.ts 0 4 projects / 4 tests 통과, explicit mock worker controller 확인
npm run e2e -- tests/e2e/server-mode-boundary.spec.ts 0 4 projects / 12 tests 통과, server mode worker 0건과 404·network no-fallback 확인
npm run e2e -- tests/e2e/smoke.spec.ts tests/e2e/auth.spec.ts tests/e2e/accessibility-shell.spec.ts 0 4 projects / 20 tests 통과
git diff --check && git diff --cached --check 0 whitespace 오류 0건

자동화는 모두 최종 통과했다. 아래 확정 항목은 현재 test가 잘못된 결과를 성공 조건으로 삼거나, 필요한 음성 조건을 검사하지 않아 통과 결과만으로 발견하지 못했다.

4.4 Mock handler 런타임 재현

Vite SSR로 실제 src/shared/mocks/handlers.ts를 로드하고 MSW Node server에 연결해 wrong origin과 logout token 상태를 확인했다.

node --input-type=module -e 'import { createServer as createViteServer } from "vite"; import { setupServer } from "msw/node"; const vite = await createViteServer({ appType: "custom", server: { middlewareMode: true, hmr: false } }); try { const { createMockHandlers, createMockStore } = await vite.ssrLoadModule("/src/shared/mocks/handlers.ts"); const mockServer = setupServer(...createMockHandlers(createMockStore())); mockServer.listen({ onUnhandledRequest: "error" }); try { const request = (path, token) => fetch(`https://wrong-origin.example${path}`, token ? { method: "POST", headers: { Authorization: `Bearer ${token}` } } : { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "admin@test.com", password: "password" }) }); const wrongOriginLogin = await request("/admin/member/login"); const invalidLogout = await request("/member/logout", "invalid-token"); const firstLogout = await request("/member/logout", "mock-admin-jwt"); const revokedLogout = await request("/member/logout", "mock-admin-jwt"); console.log(JSON.stringify({ wrongOriginLogin: wrongOriginLogin.status, invalidLogout: invalidLogout.status, firstLogout: firstLogout.status, revokedLogout: revokedLogout.status })); } finally { mockServer.close(); } } finally { await vite.close(); }'

결과:

{"wrongOriginLogin":200,"invalidLogout":200,"firstLogout":200,"revokedLogout":200}

명령은 exit 0으로 끝났다. sandbox가 Vite HMR WebSocket 0.0.0.0:24678 listen을 EPERM으로 거부했다는 경고가 있었지만, MSW가 처리한 네 HTTP status 재현은 완료됐다.

4.5 2차 독립 재검증 — 2026-07-27

명령 또는 검증 종료 결과와 핵심 증거
npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx 0 7 files / 23 tests 통과
npm run test:run 0 34 files / 136 tests 통과
npm run typecheck 0 TypeScript 오류 0건
npm run lint 0 ESLint 오류 0건
npm run build:dev 0 159 modules, JS 294.66 kB, gzip 88.66 kB
npm run build:prod 0 159 modules, JS 294.65 kB, gzip 88.66 kB
VITE_API_MODE=mock npm run build:prod 1 기대한 production guard. VITE_API_MODE=mock is only available during development 확인
test ! -e dist/mockServiceWorker.js 0 production 산출물에 worker 파일 없음
rg -l -e startMockWorker -e 'mockServiceWorker\.js' dist 1 기대한 no-match. production JS에 mock bootstrap 문자열 없음
npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts tests/e2e/mock-mode-boundary.spec.ts 0 4 projects / 24 tests 통과
npm run e2e -- tests/e2e/server-mode-boundary.spec.ts tests/e2e/smoke.spec.ts tests/e2e/auth.spec.ts tests/e2e/accessibility-shell.spec.ts 0 4 projects / 32 tests 통과
npm run e2e:mock 1 56 tests 중 36 통과 / 20 실패. mock mode에서 server 전용·기존 shell spec까지 함께 수집됨
npm run e2e -- --list 0 server 명령이 mock 전용 spec을 포함한 6 files / 56 tests를 수집
npm run e2e:mock -- --list 0 mock 명령도 같은 6 files / 56 tests를 수집
git diff --check HEAD 0 whitespace 오류 0건

표준 focused·전체 unit, typecheck, lint, build와 명시적으로 분리한 E2E 묶음은 통과했다. 그러나 bare E2E script는 mode별 test 선택 경계가 없어 실패했으며, 현재 자동화에 없는 아래 세 런타임 경계도 별도 재현됐다.

4.6 2차 런타임 재현

동일 세션 보호 route 재진입

로컬 server mode를 열고 Playwright one-off script에서 같은 AuthSessionRecord를 유지한 채 다음 순서로 실행했다.

  1. /ai-characters 최초 진입의 StrictMode 2회 probe를 모두 200으로 응답한다.
  2. SPA history로 /login에 이동하되 session을 지우지 않는다.
  3. SPA history로 /ai-characters에 재진입하고 다음 probe를 404로 응답한다.
  4. 오류 alert가 표시된 뒤 보호 main과 로그아웃 버튼 수를 확인한다.

결과:

{"firstEntry":{"probes":2,"protectedMainCount":1},"probes":3,"path":"/ai-characters","protectedMainCount":1,"logoutCount":1,"alert":"보호 route 확인에 실패했습니다."}

Mock Preview 접근 거부 화면

mock mode에서 sessionStorage{ token: "mock-member-jwt", role: "ADMIN" }을 저장하고 /ai-characters에 진입했다. 403 처리 후 결과는 다음과 같았다.

{"path":"/access-denied","mockBannerCount":0,"heading":"접근 권한이 없습니다"}

Auth handler status·media type

Vite SSR로 현재 createMockHandlers(createMockStore(), "https://api.example.com")를 MSW Node server에 연결해 비ADMIN logout과 text/plain login을 호출했다.

{"memberLogout":401,"textPlainLogin":200}

API Contract §1.2의 비ADMIN JWT 403과 지원하지 않는 media type 415에 모두 어긋난다.

4.7 3차 독립 재검증 — 2026-07-27

명령 또는 검증 종료 결과와 핵심 증거
npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx 0 7 files / 25 tests 통과
npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts 0 문서 반영 후 1 file / 3 tests 통과
npm run test:run 0 34 files / 141 tests 통과
npm run typecheck 0 TypeScript 오류 0건
npm run lint 0 ESLint 오류 0건
npm run build:dev, npm run build:prod 0 development·production build 모두 160 modules 변환 성공
VITE_API_MODE=mock npm run build:prod 1 기대한 production guard. VITE_API_MODE=mock is only available during development 확인
test ! -e dist/mockServiceWorker.js 0 production 산출물에 worker 파일 없음
rg -l -e startMockWorker -e 'mockServiceWorker\.js' dist 1 기대한 no-match. production JS에 mock bootstrap 문자열 없음
npm run e2e 0 server mode 4 projects / 32 tests 통과
npm run e2e:mock 0 mock mode 4 projects / 24 tests 통과
git diff HEAD --name-only | wc -l 0 현재 변경 37개 경로. review 상단·종료 판정의 36개 기록과 불일치 확인
git diff --check HEAD 0 whitespace 오류 0건

Mock Preview를 320px에서 열어 ADMIN login 후 mobile menu를 열고 1,200px로 viewport를 바꾸는 Chromium one-off 검증 결과는 다음과 같았다.

{
  "responses": [
    { "path": "/admin/member/login", "fromServiceWorker": true },
    { "path": "/api/v2/admin/ai-characters?page=0&size=20", "fromServiceWorker": true }
  ],
  "before": {
    "bannerInsideInert": false,
    "bannerAriaHidden": null,
    "mainInsideInert": true,
    "overlayDisplay": "block"
  },
  "after1200px": {
    "bannerInsideInert": false,
    "bannerAriaHidden": null,
    "mainInsideInert": true,
    "mainInertAncestor": "true",
    "overlayDisplay": "none"
  }
}

두 API 응답은 실제로 Service Worker에서 반환되어 backend 요청 0건 경계는 확인됐다. 반면 menu open 상태에서 banner는 background inert 경계 밖에 남았고, desktop 전환 후 overlay만 숨겨진 채 main의 inert·aria-hidden이 유지됐다.

추가 코드·test 대조에서 ProtectedRouteErrorPage는 404·network probe 실패 시 alert만 렌더하고 retry·login 이동 control을 제공하지 않으며, 기존 App/server boundary test도 shell 비노출만 확인한다는 복구 공백을 확인했다.

4.8 4차 독립 재검증 — 2026-07-27

명령 또는 검증 종료 결과와 핵심 증거
npm run test:run -- src/app/App.test.tsx src/shared/ui/__tests__/mock-mode-banner.test.tsx 0 2 files / 28 tests 통과
npm run test:run 0 34 files / 145 tests 통과
npm run typecheck, npm run lint 0 TypeScript·ESLint 오류 0건
npm run build:dev, npm run build:prod 0 development·production 모두 160 modules 변환 성공
VITE_API_MODE=mock npm run build:prod 1 기대한 production guard. VITE_API_MODE=mock is only available during development 확인
test ! -e dist/mockServiceWorker.js 0 production 산출물에 worker 파일 없음
rg -l -e startMockWorker -e 'mockServiceWorker\.js' dist 1 기대한 no-match. production JS에 mock bootstrap 문자열 없음
npm run e2e 0 server mode 4 projects / 32 tests 통과
npm run e2e:mock 0 mock mode 4 projects / 28 tests 통과
git diff HEAD --name-only | wc -l 0 tracked diff 37개 경로
git status --porcelain=v1 | wc -l 0 working tree 38개 변경 항목
git ls-files --others --exclude-standard 0 src/app/protected-admin-shell.tsx 1개가 untracked임을 확인
npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts 0 file filter와 script 고정 목록이 합쳐져 4 files / 32 tests 수집
VITE_API_MODE=server npx playwright test tests/e2e/server-mode-boundary.spec.ts --list 0 실제 boundary spec은 1 file / 12 tests이며 P2-R10 기대 16 tests에 미달

보호 route retry pending은 sandbox의 local listen·Chromium launch가 각각 EPERM·permission denied로 한 차례 실패한 뒤 허용된 로컬 실행으로 재검증했다. 404 화면에서 retry를 누르고 성공 응답을 350ms 지연한 결과는 다음과 같았다.

{
  "retryRequests": 1,
  "pending": {
    "rootText": "",
    "rootChildCount": 0,
    "mainCount": 0,
    "statusCount": 0,
    "alertCount": 0,
    "activeElement": "BODY"
  },
  "complete": {
    "mainCount": 1,
    "logout": true
  }
}

P2-R9의 banner inert·desktop breakpoint 수정과 P2-R10의 fail-closed retry 성공 경계는 unit과 4-browser E2E에서 유지됐다. 반면 untracked 파일을 제외한 범위 집계, pending 중 빈 root, hard-coded npm E2E script가 focused filter를 무효화해 network retry E2E 공백을 가리는 세 문제를 추가로 확정했다.

4.9 5차 독립 재검증 — 2026-07-27

명령 또는 검증 종료 결과와 핵심 증거
npm run test:run -- src/app/App.test.tsx 0 1 file / 27 tests 통과. 최초 probe pending 전용 test 1건과 기존 404 retry test의 pending assertion을 확인
npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts 0 2 files / 6 tests 통과
npm run test:run 0 34 files / 147 tests 통과
npm run typecheck, npm run lint 0 TypeScript·ESLint 오류 0건
npm run build:dev, npm run build:prod 0 development·production 각각 160 modules 변환 성공
VITE_API_MODE=mock npm run build:prod 1 기대한 production guard. VITE_API_MODE=mock is only available during development 확인
npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts 0 1 file / 16 tests 수집
npm run e2e:mock -- --list tests/e2e/mock-preview-shell.spec.ts 0 1 file / 24 tests 수집
npm run e2e -- tests/e2e/server-mode-boundary.spec.ts 0 4 projects / 16 tests 통과. retry pending status와 network 재실패 경계 유지
npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts 0 4 projects / 24 tests 통과
npm run e2e, npm run e2e:mock 0 bare server 36 tests, bare mock 28 tests 통과
git diff HEAD --name-only | wc -l 0 tracked diff 37개 경로
git status --short --untracked-files=all | wc -l 0 working tree 38개 변경 항목
git ls-files --others --exclude-standard 0 untracked src/app/protected-admin-shell.tsx 1개 확인

구현 대조 결과, 보호 route 최초 probe와 retry pending 동작은 각각 unit assertion과 server boundary E2E로 유지됐다. 그러나 P2-R12는 “두 test 추가”와 App 28 tests 이상을 완료 조건으로 체크한 반면 실제 App test는 27건이고, 구조도 최초 pending 전용 test 1건 추가 + 기존 retry test 보강이다. 이 완료 증거 불일치를 REV-P2-017로 확정했다.

4.10 6차 독립 재검증 — 2026-07-27

명령 또는 검증 종료 결과와 핵심 증거
npm run test:run 0 34 files / 147 tests 통과
npm run typecheck, npm run lint 0 TypeScript·ESLint 오류 0건
npm run build:dev, npm run build:prod 0 development·production 각각 160 modules 변환 성공
VITE_API_MODE=mock npm run build:prod 1 기대한 production guard. VITE_API_MODE=mock is only available during development 확인
npm run e2e 0 최초 sandbox listen EPERM 뒤 로컬 실행 권한으로 재실행해 4 projects / 36 tests 통과
npm run e2e:mock 0 최초 sandbox listen EPERM 뒤 로컬 실행 권한으로 재실행해 4 projects / 28 tests 통과
git diff --check HEAD 0 review 문서 반영 전 whitespace 오류 0건
review 현재 상태 대조 0 §5·§6·§8은 P2-R8~P2-R13 수정 완료, §7은 같은 Task를 “아직 수정하지 않았다”고 표시하는 모순 2건 확인
npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts 0 review·plan 반영 후 1 file / 3 tests 통과
plan/review 대상 git diff --check 0 review·plan 반영 후 whitespace 오류 0건

Chromium one-off 검증에서는 mock login과 보호 route 응답이 모두 fromServiceWorker=true였고, 320px의 mobile menu·logout button 높이가 각각 60px이었다. 열린 mobile menu에서 AI 캐릭터 link를 선택한 뒤 menu와 background inert 잔존은 0건이었다. 새 제품 동작 회귀는 재현되지 않았다.

4.11 7차 독립 재검증 — 2026-07-27

명령 또는 검증 종료 결과와 핵심 증거
Phase 2 완료 상태 exact 검색 1 상단 현재 상태에 P2-R15 완료와 Phase 3 진행 가능 판정이 없음
tail -n 16 plan-task.md 최신 Progress 검색 1 문서 끝이 6차 재검증의 P2-R15 수정 필요 상태로 끝남
P2-R15 Task·review 상태 대조 0 Task 체크·inline 검증과 review §5·§6·§7·§8·§9는 수정 완료 및 열린 항목 없음을 표시
git diff --check HEAD 0 review 반영 전 working tree whitespace 오류 0건

제품 코드와 실행 설정의 새 변경은 없다. 완료 상태의 source인 Phase 상단과 최신 누적 Progress가 소비자인 Phase 3 시작 판정으로 전파되지 않은 문서 정합성 문제를 REV-P2-019로 확정했다.

5. 발견 사항 요약

ID 심각도 상태 제목 소유 Task 후속 goal
REV-P2-001 High 수정 완료 404·network probe 실패 뒤 미검증 보호 shell을 렌더링 R2.1 P2-R1
REV-P2-002 High 수정 완료 mock handler가 API base URL이 아닌 모든 origin을 허용 R2.2 P2-R2
REV-P2-003 High 수정 완료 invalid·revoked JWT logout을 성공 응답으로 정규화 R2.2 P2-R2
REV-P2-004 Low 수정 완료 보호 shell 빈 상태가 Character 연결 시점을 완료된 Phase 2로 표시 R2.3 P2-R3
REV-P2-005 Low 수정 완료 Phase 2 Files·하단 Progress가 실제 구현과 불일치 R2.3 P2-R3
REV-P2-006 High 수정 완료 한 번 검증된 동일 세션의 재진입 probe 실패가 보호 shell을 다시 노출 R2.4 P2-R4
REV-P2-007 High 수정 완료 auth fixture가 비ADMIN status와 login media type 계약을 완화 R2.5 P2-R5
REV-P2-008 Medium 수정 완료 Mock Preview 403·보호 오류 화면에서 지속 안내가 사라짐 R2.6 P2-R6
REV-P2-009 Medium 수정 완료 bare server·mock E2E 명령이 반대 mode spec까지 함께 수집 R2.7 P2-R7
REV-P2-010 Low 수정 완료 Phase 2 완료 문서가 P2-R4~P2-R7 변경과 불일치 R2.8 P2-R8
REV-P2-011 Medium 수정 완료 열린 mobile menu가 desktop 전환 후 숨은 overlay와 inert shell을 남김 R2.9 P2-R9
REV-P2-012 Medium 수정 완료 열린 mobile menu의 background inert 경계에서 Mock Preview banner가 제외됨 R2.9 P2-R9
REV-P2-013 Medium 수정 완료 보호 route 오류 화면에 fail-closed 재시도·복구 경로가 없음 R2.10 P2-R10
REV-P2-014 Low 수정 완료 working tree 범위 집계가 untracked 보호 shell을 제외 R2.11 P2-R11
REV-P2-015 Medium 수정 완료 보호 route probe pending 중 root가 비어 진행 상태를 알리지 않음 R2.12 P2-R12
REV-P2-016 Medium 수정 완료 고정 E2E script가 focused 필터와 network retry 증거 공백을 가림 R2.13 P2-R13
REV-P2-017 Low 수정 완료 P2-R12 test 증거가 실제 test 구조·실행 수와 불일치 R2.14 P2-R14
REV-P2-018 Low 수정 완료 plan 전환 절이 완료된 P2-R8~P2-R13을 미수정으로 표시 R2.15 P2-R15
REV-P2-019 Low 수정 완료 Phase 2 현재 상태·최신 Progress가 P2-R15 완료와 불일치 R2.16 P2-R16

1차 확정 5건, 2차 확정 4건, 3차 확정 4건, 4차 확정 3건, 5차·6차·7차 확정 Low 각 1건은 2026-07-27 후속 수정으로 완료됐다. 오탐·보류 항목은 없다.

6. 발견 사항 상세

REV-P2-001 — 404·network probe 실패 뒤 미검증 보호 shell을 렌더링

  • 심각도: High
  • 상태: 수정 완료
  • 관련 요구사항: AUTH-003, MOCK-003
  • 관련 계약: API Contract §3.3, Phase 1 P1-R3
  • 소유 Task: 신규 R2.1, goal P2-R1

관찰 내용

저장된 ADMIN session으로 /ai-characters에 진입해 현재 DB 권한 확인용 목록 probe가 404 또는 network error로 실패하면 routeError가 설정된다. 렌더 guard는 routeError === null일 때만 shell을 숨기므로, probe가 성공하지 않았는데도 sidebar·header·logout·보호 main을 포함한 ProtectedAdminShell을 렌더한다.

근거

  • 코드: src/app/App.tsx:183-218은 비401·비403 실패를 routeError로 저장하고, src/app/App.tsx:243-251은 오류가 있으면 token 검증 성공 여부와 무관하게 shell을 반환한다.
  • 코드: src/app/admin-pages.tsx:19-35의 alert는 ProtectedAdminShell 내부 main에서만 렌더된다.
  • 테스트: tests/e2e/server-mode-boundary.spec.ts:28-63은 404·network error에서 이 내부 alert가 보이는 것을 성공 조건으로 사용하며 실제 12 tests가 통과했다.
  • 회귀 기준: src/app/App.test.tsx:196-227과 plan P1-R3은 stale ADMIN probe pending·403 동안 보호 shell 비노출을 고정한다.

재현 또는 검증 절차

  1. sessionStorage{ token: "admin-token", role: "ADMIN" } session을 둔다.
  2. GET /api/v2/admin/ai-characters?page=0&size=20을 404로 응답하거나 network abort한다.
  3. /ai-characters에 진입하면 보호 route 확인에 실패했습니다. alert와 함께 보호 shell main이 렌더된다.
  4. 요구되는 결과는 probe 성공 전에는 보호 shell을 숨기고, no-fallback 오류를 shell 밖의 독립 오류 UI로 표시하는 것이다.

영향

현재 DB role을 확인할 수 없는 실패 상태를 ADMIN으로 간주해 fail-closed 보호 경계가 깨진다. 현재 Phase에는 실제 도메인 데이터·mutation이 없지만 Phase 3부터 같은 shell 아래 기능이 추가되면 일시적 404·network failure가 보호 navigation과 action surface 노출로 이어질 수 있다.

권장 조치

현재 token의 probe 성공 여부와 route 오류 표시 상태를 분리한다. 성공한 현재 token만 ProtectedAdminShell을 열고, 404·network 오류는 shell 밖에서 표시한다. 404·network와 이전 오류 뒤 새 session probe pending을 App test에 추가하고 server boundary E2E가 worker 0건과 보호 shell 비노출을 함께 확인하게 한다.

판정 기록

  • 2026-07-27 — 코드 분기와 4-browser server boundary E2E를 대조해 보호 route 계약 회귀로 확정했다.

REV-P2-002 — mock handler가 API base URL이 아닌 모든 origin을 허용

  • 심각도: High
  • 상태: 수정 완료
  • 관련 요구사항: MOCK-002
  • 관련 계약: API Contract §1.5
  • 소유 Task: 신규 R2.2, goal P2-R2

관찰 내용

login, logout, AI Character probe handler가 모두 */... wildcard URL을 사용한다. 따라서 앱의 VITE_API_BASE_URL이 잘못되거나 request가 다른 origin으로 나가도 mock mode는 같은 성공 fixture를 반환한다.

근거

  • 코드: src/shared/mocks/handlers.ts:97-120의 세 handler가 exact API base URL 대신 */admin/member/login, */member/logout, */api/v2/admin/ai-characters에 매칭된다.
  • 계약: API Contract §1.5는 mock mode도 production과 같은 URL을 사용하도록 요구한다.
  • 실행: https://wrong-origin.example/admin/member/login에 유효 body를 전송한 런타임 재현이 200을 반환했다.
  • 테스트 공백: auth handler test는 https://api.example.com 한 origin만 사용하고 wrong origin이 unhandled인지 확인하지 않는다.

재현 또는 검증 절차

  1. createMockHandlers(createMockStore())를 MSW server에 등록한다.
  2. 설정된 API base와 무관한 https://wrong-origin.example/admin/member/login에 유효 JSON을 보낸다.
  3. 실제 결과는 200 ADMIN fixture다.
  4. 요구되는 결과는 handler 비매칭이며 browser mock mode에서는 onUnhandledRequest: "error" 경계가 잘못된 URL을 드러내는 것이다.

영향

잘못된 API host, base URL 또는 URL 조합 회귀가 mock happy path에서 숨겨진다. mock Gate가 실제 server integration 완료 증거는 아니더라도, 동일 production URL 경계를 검증한다는 MOCK-002의 신뢰성이 사라진다.

권장 조치

createMockHandlers에 검증된 API base URL을 주입하고 new URL(path, apiBaseUrl)의 exact URL로 handler를 등록한다. wrong-origin request가 handler에 잡히지 않는 focused test를 추가한다.

판정 기록

  • 2026-07-27 — wildcard 코드와 wrong-origin 200 런타임 결과로 API Contract 위반을 확정했다.

REV-P2-003 — invalid·revoked JWT logout을 성공 응답으로 정규화

  • 심각도: High
  • 상태: 수정 완료
  • 관련 요구사항: MOCK-002, AUTH-013
  • 관련 계약: API Contract §1.2, §3.2~3.3
  • 소유 Task: 신규 R2.2, goal P2-R2

관찰 내용

logout handler는 Bearer 문자열 존재와 body 없음만 검사한 뒤 token 유효성·폐기 상태를 확인하지 않고 store에 추가하고 200을 반환한다. 임의 token과 이미 revoke된 admin token의 두 번째 logout도 성공한다.

근거

  • 코드: src/shared/mocks/handlers.ts:107-118getTokenAccess를 호출하지 않고 모든 non-empty token을 revoke한 뒤 ok({})를 반환한다.
  • 계약: API Contract §1.2는 잘못됨·만료·폐기 JWT를 401로 규정한다.
  • 실행: invalid token logout과 정상 logout 뒤 같은 admin token의 재logout이 모두 200이었다.
  • 테스트 공백: src/shared/mocks/__tests__/auth-handlers.test.ts:65-82는 Bearer 없음과 body 있음만 확인한다.

재현 또는 검증 절차

  1. 새 mock store에서 Bearer invalid-token으로 POST /member/logout을 호출한다.
  2. Bearer mock-admin-jwt로 정상 logout한 뒤 같은 token으로 다시 호출한다.
  3. 실제 결과는 두 요청 모두 200 success envelope다.
  4. 요구되는 결과는 invalid·revoked JWT에 401 error envelope를 반환하는 것이다.

영향

mock preview에서 서버 logout 실패 경고와 폐기 token 계약을 검증할 수 없고, 실제 서버에서 발생할 401을 성공으로 오인한다. AUTH-013의 로컬 session 제거는 유지되더라도 실패 피드백 QA가 왜곡된다.

권장 조치

store mutation 전에 token 상태를 판정하고 invalid·revoked token은 API Contract의 401 envelope로 반환한다. 정상 login → logout → login 회귀와 invalid·double logout 경계 test를 함께 둔다. 공통 logout의 다른 회원 role 의미는 제공 계약 이상으로 추정하지 않는다.

판정 기록

  • 2026-07-27 — invalid·revoked token 200을 런타임으로 재현해 오류 status 계약 위반을 확정했다.

REV-P2-004 — 보호 shell 빈 상태가 Character 연결 시점을 완료된 Phase 2로 표시

  • 심각도: Low
  • 상태: 수정 완료
  • 관련 요구사항: Phase 지도와 사용자 상태 안내
  • 관련 계약: 없음
  • 소유 Task: 신규 R2.3, goal P2-R3

관찰 내용

현재 plan에서 Character workspace는 Phase 3이지만 보호 shell의 설명과 빈 상태 제목은 모두 “Phase 2에서 AI 캐릭터 목록이 연결됩니다.”라고 표시한다. Phase 2를 완료한 현재 화면에서도 다음 작업 시점을 잘못 안내한다.

근거

  • 코드: src/app/admin-pages.tsx:27,34의 사용자 표시가 Phase 2를 가리킨다.
  • 문서: plan-task.md Phase 지도와 ## Phase 3. Character workspace vertical slice는 Character 목록/workspace를 Phase 3 소유로 둔다.
  • 테스트: src/app/App.test.tsx도 기존 Phase 2 문구를 assertion해 잘못된 표시를 고정한다.

재현 또는 검증 절차

  1. ADMIN으로 /ai-characters 보호 shell을 연다.
  2. 설명과 empty state 제목에서 Phase 2 안내를 확인한다.
  3. 실제 결과는 완료된 Phase 2를 다음 연결 시점으로 표시한다.
  4. 요구되는 결과는 현재 계획의 Phase 3 안내다.

영향

기능 자체는 막지 않지만 운영자·reviewer가 Phase 2 완료 상태와 Character 구현 범위를 잘못 이해할 수 있다.

권장 조치

두 사용자 문구와 관련 App test를 Phase 3으로 갱신한다. Phase 3 기능 자체는 이 수정에 포함하지 않는다.

판정 기록

  • 2026-07-27 — 실제 UI 문자열과 Phase 지도를 대조해 문서·사용자 안내 불일치로 확정했다.

REV-P2-005 — Phase 2 Files·하단 Progress가 실제 구현과 불일치

  • 심각도: Low
  • 상태: 수정 완료
  • 관련 요구사항: plan-task.md 구현 완료 정의의 문서·구현 차이 0건, Goal 실행형 계획 유지보수 규칙
  • 관련 계약: 없음
  • 소유 Task: 신규 R2.3, goal P2-R3

관찰 내용

Phase 2 주요 Files는 존재하지 않는 src/app/providers.tsx, fixtures.ts, store.ts, handlers.test.ts, store.test.ts를 완료 산출물처럼 유지하고, 실제 App.tsx, vite.config.ts, playwright.config.ts, contract.ts, auth/docs/production test와 두 boundary E2E를 기록하지 않는다. 또한 리뷰 시작 시 하단 ## 7. 검증 기록의 마지막 항목은 Phase 2 착수 전 계획 추가 기록이었고, 완료한 Phase 2 구현의 무엇을/왜/실제 명령·결과가 누적되지 않았다.

근거

  • 문서: plan-task.md Phase 2 주요 Files와 실제 staged 28개 경로가 일치하지 않는다.
  • 파일 검사: src/app/providers.tsx, src/shared/mocks/fixtures.ts, store.ts, __tests__/handlers.test.ts, store.test.ts는 존재하지 않는다.
  • 실제 구현: src/app/App.tsx, vite.config.ts, playwright.config.ts, src/shared/mocks/contract.ts, 세부 contract/docs/production test와 mock-mode-boundary.spec.ts, server-mode-boundary.spec.ts가 Phase 2 변경에 포함된다.
  • 문서: Task·Gate별 실행 기록은 Phase 본문에 있으나 하단 Progress에는 구현 완료 기록이 없었다. 이 리뷰 기록은 새로 누적했지만 실제 Phase 2 구현 Progress와 Files 정정은 아직 수행하지 않았다.

재현 또는 검증 절차

  1. Phase 2 주요 Files의 각 경로 존재 여부를 검사한다.
  2. git diff --cached --name-status의 실제 구현 경로와 대조한다.
  3. plan-task.md 하단 ## 7. 검증 기록의 마지막 구현 기록을 확인한다.
  4. 실제 결과는 Files와 Progress 모두 현재 Phase 2 구현과 다르다.

영향

독립 reviewer가 어떤 파일이 Phase 2 소유인지, 어떤 최종 명령으로 완료했는지 plan의 표준 위치에서 재현하기 어렵다. 이후 goal 실행자가 존재하지 않는 파일을 전제로 작업할 수 있다.

권장 조치

Phase 2 주요 Files를 실제 최소 구조로 갱신하고, 하단 Progress에 기존 본문 기록을 지우지 않은 채 무엇을/왜/실제 명령·결과/남은 항목을 누적한다. 사용자 Phase 문구 정정과 함께 문서/App focused test를 실행한다.

판정 기록

  • 2026-07-27 — 파일 존재 검사, staged name-status와 하단 Progress를 교차 확인해 문서 정합성 문제로 확정했다.

REV-P2-006 — 한 번 검증된 동일 세션의 재진입 probe 실패가 보호 shell을 다시 노출

  • 심각도: High
  • 상태: 수정 완료
  • 관련 요구사항: AUTH-003, MOCK-003
  • 관련 계약: API Contract §3.3, 기존 P2-R1
  • 소유 Task: R2.4, goal P2-R4

관찰 내용

같은 session 객체가 한 번 보호 route probe를 통과하면 verifiedProtectedRouteSession이 유지된다. session을 지우지 않고 /login을 거쳐 /ai-characters에 다시 들어올 때 새 probe가 시작돼도 이전 검증 값과 현재 session이 이미 같으므로 보호 shell을 즉시 렌더한다. 두 번째 probe가 404 또는 network error로 실패해도 검증 값은 무효화되지 않아 오류 alert를 보호 shell 내부에 표시한다.

근거

  • 코드: src/app/App.tsx:189-190이 오류와 검증 session을 component state로 보관한다.
  • 코드: src/app/App.tsx:198-233은 새 probe 시작 시 기존 verifiedProtectedRouteSession을 무효화하지 않는다.
  • 코드: src/app/App.tsx:258-264은 이전 검증 session과 현재 session의 객체 동일성만으로 shell 렌더를 허용한다.
  • 테스트 공백: src/app/App.test.tsx:208-294는 최초 probe 실패와 logout → 새 session 재로그인은 다루지만, 성공한 동일 session의 route 이탈 → 재진입 실패는 다루지 않는다.
  • 런타임: 최초 probe 2회 200 뒤 동일 session 재진입 probe를 404로 응답했을 때 protectedMainCount=1, logoutCount=1이었다.

재현 또는 검증 절차

  1. ADMIN session으로 /ai-characters probe를 성공시켜 보호 shell을 연다.
  2. session을 유지한 채 SPA navigation으로 /login에 이동한다.
  3. /ai-characters로 다시 이동하고 새 probe를 404 또는 network error로 실패시킨다.
  4. 실제 결과는 보호 route 확인에 실패했습니다. alert와 보호 main·로그아웃 버튼이 함께 남는다.
  5. 요구되는 결과는 새 probe 성공 전과 실패 후에 보호 shell이 노출되지 않는 것이다.

영향

현재 DB ADMIN 여부를 다시 확인하지 못한 상태에서도 이전 성공을 재사용해 보호 navigation과 action surface를 노출한다. Phase 3 이후 mutation UI가 같은 shell에 추가되면 P2-R1의 fail-closed 보장이 route 재진입 경로에서 다시 깨진다.

권장 조치

검증 상태를 session 객체뿐 아니라 현재 probe 시도에 귀속하고, route 재진입 시 이전 성공을 먼저 무효화한다. 성공 → route 이탈 → 동일 session 재진입 pending·404·network·403을 하나의 회귀 test cycle로 고정한다.

판정 기록

  • 2026-07-27 — 코드 상태 전이와 Chromium one-off Playwright 재현 결과로 확정했다.
  • 2026-07-27 — P2-R4에서 route visit key 회귀 test와 server E2E로 수정 완료를 확인했다.

REV-P2-007 — auth fixture가 비ADMIN status와 login media type 계약을 완화

  • 심각도: High
  • 상태: 수정 완료
  • 관련 요구사항: AUTH-006, AUTH-008, MOCK-002
  • 관련 계약: API Contract §1.2, §3.1~3.3
  • 소유 Task: R2.5, goal P2-R5

관찰 내용

logout handler는 getTokenAccess(token) !== "admin"인 모든 token을 401로 합쳐 이미 denied로 분류한 비ADMIN token도 403 대신 401을 반환한다. login handler는 request의 Content-Type을 확인하지 않고 request.json() 결과만 파싱해 text/plain JSON body도 200 ADMIN 응답으로 처리한다.

근거

  • 코드: src/shared/mocks/handlers.ts:37-45memberTokendenied로 분류한다.
  • 코드: src/shared/mocks/handlers.ts:114-125는 logout에서 deniedunauthorized를 모두 401로 반환한다.
  • 코드: src/shared/mocks/handlers.ts:85-99는 login Authorization과 JSON shape만 검사하고 media type을 확인하지 않는다.
  • 계약: API Contract §1.2는 JWT role 비ADMIN을 403, 지원하지 않는 media type을 415로 규정한다. §3.1은 login Content-Type: application/json을 요구한다.
  • 테스트 공백: auth-handlers.test.ts의 403 test는 Character probe만 확인하고 비ADMIN logout·잘못된 login media type을 확인하지 않는다.
  • 런타임: 현재 handler는 비ADMIN logout 401, text/plain login 200을 반환했다.

재현 또는 검증 절차

  1. Bearer mock-member-jwtPOST /member/logout을 body 없이 호출한다.
  2. Content-Type: text/plain과 유효한 JSON 문자열 body로 POST /admin/member/login을 호출한다.
  3. 실제 결과는 각각 401과 200이다.
  4. 요구되는 결과는 API Contract에 따른 403과 415 오류 envelope다.

영향

mock mode가 실제 서버보다 넓은 요청을 성공시키고 오류 status를 다르게 반환한다. auth serializer·권한 처리 회귀가 mock happy path에서 숨겨져 MOCK-002의 production 동일 계약 검증 신뢰성이 낮아진다.

권장 조치

logout에서도 공통 token access 분기를 재사용해 deniedunauthorized를 구분하고, login JSON 파싱 전에 media type을 검증한다. 비ADMIN logout 403과 text/plain·누락·잘못된 media type 경계 test를 추가한다.

판정 기록

  • 2026-07-27 — API Contract와 handler 분기를 대조하고 MSW Node runtime status를 재현해 확정했다.
  • 2026-07-27 — P2-R5에서 비ADMIN logout 403과 non-JSON login 415 test로 수정 완료를 확인했다.

REV-P2-008 — Mock Preview 403·보호 오류 화면에서 지속 안내가 사라짐

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: MOCK-008
  • 관련 계약: API Contract §1.5
  • 소유 Task: R2.6, goal P2-R6

관찰 내용

mock banner는 login과 검증 성공 후 ProtectedAdminShell에만 배치된다. 보호 probe가 403으로 전환한 AccessDeniedPage와 404·network error를 표시하는 ProtectedRouteErrorPage에는 banner가 없어 mock mode 화면이 실제 서버 화면처럼 보인다.

근거

  • 코드: src/app/App.tsx:235-247, 264는 login과 보호 shell에 apiMode를 전달한다.
  • 코드: src/app/App.tsx:254-261은 접근 거부·보호 오류 화면을 banner 없이 직접 반환한다.
  • 요구사항: MOCK-008은 mock mode 화면에 실제 서버가 아니라는 지속적으로 보이는 안내를 요구한다.
  • 테스트 공백: App·mock E2E는 login과 성공 shell의 banner만 확인하고 403·404·network 상태는 확인하지 않는다.
  • 런타임: mock mode에서 stale ADMIN 역할을 나타내는 mock-member-jwt로 진입한 /access-deniedmockBannerCount가 0이었다.

재현 또는 검증 절차

  1. mock mode에서 sessionStorage{ token: "mock-member-jwt", role: "ADMIN" }을 저장한다.
  2. /ai-characters에 진입해 mock handler의 403 응답을 받는다.
  3. /access-denied heading은 보이지만 Mock Preview status는 존재하지 않는다.
  4. 요구되는 결과는 보호 shell을 노출하지 않으면서도 mock mode 안내는 지속되는 것이다.

영향

권한·연결 오류를 QA하는 사용자가 현재 응답이 mock fixture에서 나온 것인지 실제 서버에서 나온 것인지 구분하기 어렵다. 성공 화면보다 오류 화면에서 환경 혼동 가능성이 커진다.

권장 조치

Mock Preview 안내를 route별 page 내부가 아니라 모든 mock route state를 감싸는 공통 상위 경계에 한 번만 배치하고, login·성공 shell·403·404/network 상태 test를 추가한다.

판정 기록

  • 2026-07-27 — 코드 composition과 Chromium mock runtime 결과로 확정했다.
  • 2026-07-27 — P2-R6에서 mock 403·보호 오류 banner test와 mock E2E로 수정 완료를 확인했다.

REV-P2-009 — bare server·mock E2E 명령이 반대 mode spec까지 함께 수집

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: MOCK-001, Phase 2 P2-T1, 실행 스크립트·README 동기화
  • 관련 계약: 없음
  • 소유 Task: R2.7, goal P2-R7

관찰 내용

npm run e2enpm run e2e:mock은 환경 변수와 web server만 바꾸고 동일한 tests/e2e 전체를 실행한다. server 명령도 mock worker 등록을 요구하는 spec을, mock 명령도 server worker 0건과 page-level route interception을 전제로 한 spec을 함께 수집한다.

근거

  • 설정: package.json:16-17의 두 script는 spec filter 없이 playwright test를 실행한다.
  • 설정: playwright.config.ts:3-22는 web server mode만 고르고 mode별 testMatch, testIgnore, project dependency를 정의하지 않는다.
  • 문서: README와 docs/agent-guide/scripts.md는 bare 두 명령을 각각 server mode와 mock mode Playwright로 안내한다.
  • 실행: 두 --list 명령이 모두 같은 6 files / 56 tests를 수집했다.
  • 실행: bare npm run e2e:mock은 server 전용·기존 shell spec 충돌로 36 통과 / 20 실패했다.
  • 대조: mode에 맞는 spec을 명시한 mock 24 tests와 server 32 tests는 각각 모두 통과했다.

재현 또는 검증 절차

  1. npm run e2e -- --listnpm run e2e:mock -- --list를 실행한다.
  2. 두 명령이 모두 mock-*.spec.tsserver-mode-boundary.spec.ts를 포함한 같은 56 tests를 출력한다.
  3. npm run e2e:mock을 bare로 실행하면 20 tests가 실패한다.
  4. 요구되는 결과는 문서의 각 bare 명령이 자기 mode에 유효한 spec 집합만 실행해 exit 0을 반환하는 것이다.

영향

표준 QA 명령이 mode별로 독립 재현되지 않고, 사용자는 계획에 숨겨진 spec 경로를 알아야만 통과 결과를 얻는다. CI나 후속 Phase가 bare script를 사용하면 정상 변경도 실패로 판정한다.

권장 조치

현재 단일 Playwright config 안에서 apiMode에 따라 가장 작은 testMatch 또는 testIgnore 경계를 두고, bare 두 명령 자체를 contract test와 Gate에 포함한다. 별도 config 복제는 필요할 때까지 만들지 않는다.

판정 기록

  • 2026-07-27 — package/config 추적, 동일 test 목록과 bare mock 실행 실패로 확정했다.
  • 2026-07-27 — P2-R7에서 mode별 script contract, --list, bare E2E 실행으로 수정 완료를 확인했다.

REV-P2-010 — Phase 2 완료 문서가 P2-R4~P2-R7 변경과 불일치

  • 심각도: Low
  • 상태: 수정 완료
  • 관련 요구사항: plan-task.md 구현 완료 정의의 문서·구현 차이 0건, Goal 실행형 계획 유지보수 규칙
  • 관련 계약: 없음
  • 소유 Task: R2.8, goal P2-R8

관찰 내용

P2-R4~P2-R7에서 새 코드·test 경로와 검증이 추가됐지만 Phase 2 주요 Files, review 상단 범위·working tree 경로 수와 종료 판정은 이전 상태를 유지한다. review는 코드·문서 정합성을 충족으로 판정하지만 현재 표준 위치만으로는 최종 변경 범위를 재현할 수 없다.

근거

  • 문서: plan-task.md:589-600주요 Filessrc/app/App.test.tsx, src/app/admin-pages.tsx, src/app/browser-location.ts, src/app/protected-admin-shell.tsx가 없다.
  • 문서: plan-task.md:829P2-R4에서 src/app/protected-admin-shell.tsx를 새로 분리했다고 기록한다.
  • 문서: 3차 반영 전 review 상단은 P2-R3까지만 대상으로 적고 변경 36개 경로라고 기록했으나 상세와 종료 판정은 P2-R7까지 완료됐다고 적었다.
  • 실행: git diff HEAD --name-only | wc -l은 37개 경로를 반환했다.
  • 규칙: docs/agent-guide/goal-plan.md는 Task별 정확한 Files·Interfaces와 범위 변경 전 plan 갱신을 요구한다.

재현 또는 검증 절차

  1. git diff HEAD --name-only로 현재 변경 경로를 수집한다.
  2. Phase 2 주요 FilesP2-R4~P2-R7 기록과 대조한다.
  3. review 상단의 대상·경로 수와 ## 8. 리뷰 종료 판정을 대조한다.
  4. 실제 결과는 코드·test 경로 누락, 대상 범위와 경로 수 불일치, 정합성 충족 판정의 상충이다.

영향

독립 실행자와 reviewer가 최종 Phase 2 소유 파일과 최신 Gate 근거를 표준 위치에서 재현할 수 없다. 기능 동작에는 직접 영향이 없지만 완료 문서의 추적성과 종료 판정 신뢰성이 낮아진다.

권장 조치

문서 전용 P2-R8에서 Phase 2 Files, 완료 Task의 Files·Interfaces, review 범위·경로 수와 3차 검증 기록을 현재 working tree에 맞춰 누적 정정한다. 기존 완료 체크와 과거 수치는 삭제하지 않는다.

판정 기록

  • 2026-07-27 — 현재 diff 37개 경로, plan Files와 review 메타데이터를 교차 확인해 문서 추적성 문제로 확정했다.

REV-P2-011 — 열린 mobile menu가 desktop 전환 후 숨은 overlay와 inert shell을 남김

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: PRD §10.6 반응형 원칙, §10.7 접근성 최소 기준, P2-T3
  • 관련 계약: 없음
  • 소유 Task: R2.9, goal P2-R9

관찰 내용

320px에서 mobile menu를 연 뒤 viewport를 lg 이상으로 넓히면 overlay는 lg:hidden으로 사라지지만 isMobileMenuOpen은 계속 true다. 따라서 화면에 다시 나타난 desktop shell은 inertaria-hidden=true를 유지하고 navigation·logout·main을 조작할 수 없다.

근거

  • 코드: src/app/protected-admin-shell.tsx:93isMobileMenuOpen만으로 shell background에 inertaria-hidden을 적용한다.
  • 코드: src/app/protected-admin-shell.tsx:133은 overlay를 CSS lg:hidden으로만 숨기며 breakpoint 전환 시 state를 닫는 처리가 없다.
  • 테스트 공백: tests/e2e/accessibility-shell.spec.tsmock-preview-shell.spec.ts는 고정 320px menu 동작만 확인하고 열린 상태의 1,024px·1,200px 전환을 검증하지 않는다.
  • 런타임: 320px menu open에서 overlay display=block, main inert를 확인한 뒤 1,200px에서 overlay display=none, main inert ancestor의 aria-hidden=true가 그대로 남았다.

재현 또는 검증 절차

  1. Mock Preview ADMIN shell을 320px viewport로 열고 mobile menu를 연다.
  2. menu를 닫지 않고 viewport를 1,200px로 변경한다.
  3. overlay의 computed displaymain.closest('[inert]')를 확인한다.
  4. 실제 결과는 overlay display:none, main의 inert ancestor aria-hidden=true다. 요구되는 결과는 breakpoint 전환과 함께 menu state·inert·aria-hidden을 해제하는 것이다.

영향

좁은 desktop 창을 넓히거나 responsive QA 중 breakpoint를 넘으면 보이는 desktop shell 전체가 pointer·keyboard·보조기기 조작 불가 상태가 된다. 다시 좁혀 menu를 닫기 전에는 화면 안에서 복구할 수 없다.

권장 조치

native viewport query를 사용해 lg 진입 시 mobile menu state를 닫고, breakpoint 자동 종료에서는 숨겨진 trigger로 focus를 복귀하지 않는다. 320px open → 1,024px·1,200px 전환 회귀 test와 desktop control 조작 E2E를 추가한다.

판정 기록

  • 2026-07-27 — Chromium viewport 전환에서 hidden overlay와 inert shell 잔존을 직접 재현해 확정했다.
  • 2026-07-27 — P2-R9에서 matchMedia('(min-width: 1024px)') change로 menu state를 닫고 focus 복귀를 차단했다. App focused unit과 mock e2e가 1,024px·1,200px 전환 뒤 mobile overlay·inert·aria-hidden 잔존 0건을 확인해 수정 완료로 판정했다.

REV-P2-012 — 열린 mobile menu의 background inert 경계에서 Mock Preview banner가 제외됨

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: PRD §10.7 접근성 최소 기준, MOCK-008, P2-R6
  • 관련 계약: API Contract §1.5
  • 소유 Task: R2.9, goal P2-R9

관찰 내용

P2-R6에서 Mock Preview banner를 RouteFrame으로 올린 뒤 banner는 ProtectedAdminShell이 menu open 시 적용하는 background inert·aria-hidden 경계의 형제가 됐다. menu가 열려도 banner는 accessibility tree에 남아 overlay navigation과 background 상태가 동시에 노출된다.

근거

  • 코드: src/app/App.tsx:42-48RouteFrame은 banner와 ProtectedAdminShell을 형제로 렌더한다.
  • 코드: src/app/protected-admin-shell.tsx:93의 inert·aria-hidden은 shell 내부 background에만 적용된다.
  • 테스트 공백: App/mock E2E는 banner 표시와 menu focus containment을 각각 확인하지만 menu open 상태의 banner inert 귀속은 확인하지 않는다.
  • 런타임: 320px menu open에서 main은 inert 내부였지만 banner의 closest('[inert]')null, aria-hiddennull이었다.

재현 또는 검증 절차

  1. mock mode ADMIN shell에서 mobile menu를 연다.
  2. Mock Preview banner와 main 각각의 closest('[inert]')를 확인한다.
  3. 실제 결과는 main만 inert background에 포함되고 banner는 포함되지 않는다.
  4. 요구되는 결과는 시각적 banner를 유지하되 열린 navigation 외 background 전체가 같은 inert·aria-hidden 경계에 포함되는 것이다.

영향

키보드 focus는 menu 안에 머물지만 screen reader virtual cursor는 background banner를 계속 탐색할 수 있어 overlay 맥락과 background 차단 의미가 불일치한다.

권장 조치

성공 shell에서는 banner와 shell 본문을 같은 background inert container로 조합하고, login·403·보호 오류 화면의 공통 banner 배치는 유지한다. menu open 상태의 banner role 미노출과 menu close 후 복원을 unit/E2E로 고정한다.

판정 기록

  • 2026-07-27 — DOM inert 귀속과 aria-hidden 값을 Chromium에서 확인해 접근성 경계 회귀로 확정했다.
  • 2026-07-27 — P2-R9에서 성공 shell의 MockModeBannerProtectedAdminShell background inert container 안으로 이동했다. 320px menu open 상태의 unit/e2e가 banner와 main의 같은 inert·aria-hidden 경계를 확인해 수정 완료로 판정했다.

REV-P2-013 — 보호 route 오류 화면에 fail-closed 재시도·복구 경로가 없음

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: PRD §10.5 오류 화면 상태, MOCK-003, P2-R1
  • 관련 계약: API Contract §1.2, §1.5
  • 소유 Task: R2.10, goal P2-R10

관찰 내용

저장된 ADMIN session의 보호 route probe가 404 또는 network error로 실패하면 shell 밖 오류 alert만 표시된다. 오류 page에는 재시도, 로그인 이동 또는 session 종료 control이 없어 일시적인 연결 실패가 복구돼도 사용자는 browser refresh·주소 이동에 의존해야 한다.

근거

  • 코드: src/app/App.tsx:32-40ProtectedRouteErrorPage는 message를 담은 p[role=alert]만 렌더한다.
  • 코드: src/app/App.tsx:138-143은 미검증 상태에서 이 page만 반환하며 retry attempt를 시작할 action을 전달하지 않는다.
  • 요구사항: PRD §10.5는 오류 상태에 서버의 한국어 message와 재시도를 요구하고 MOCK-003은 server mode 오류를 실제 오류 UI로 처리하도록 한다.
  • 테스트 공백: src/app/App.test.tsx:224-263tests/e2e/server-mode-boundary.spec.ts:28-68은 alert와 shell·logout 비노출만 확인하고 연결 복구 뒤 사용자 retry를 검증하지 않는다.

재현 또는 검증 절차

  1. ADMIN session으로 /ai-characters에 진입하고 probe를 404 또는 network error로 실패시킨다.
  2. 보호 route 확인에 실패했습니다. alert가 표시된 뒤 button·link를 확인한다.
  3. 실제 결과는 interactive recovery control 0건이다.
  4. 요구되는 결과는 보호 shell을 계속 숨긴 채 사용자가 명시적으로 probe를 재시도하고, 성공한 현재 attempt 뒤에만 shell을 여는 것이다.

영향

일시적인 network 장애에서도 앱 내부 복구가 불가능하다. 사용자가 browser refresh를 알지 못하면 보호 화면 진입을 완료할 수 없고, 오류 상태의 접근 가능한 recovery 기준도 충족하지 못한다.

권장 조치

자동 retry나 mock fallback을 추가하지 않고 오류 page에 keyboard-accessible retry button을 제공한다. 404 → retry 성공, network → retry 실패·성공을 test하고 모든 비성공 attempt에서 shell·logout 비노출을 유지한다. 정상 오류 envelope의 한국어 message는 보존하고 network·invalid response에는 공통 안내를 사용한다.

판정 기록

  • 2026-07-27 — 오류 page 코드와 App/server boundary test를 PRD 오류 상태 기준에 대조해 복구 경로 누락으로 확정했다.
  • 2026-07-27 — P2-R10에서 retry attempt state와 native retry button을 추가했다. 404 → retry 성공, network → retry 실패 unit과 server mode E2E가 성공 전 shell·logout 비노출, 서버 message 보존, mock worker 0건을 확인해 수정 완료로 판정했다.

REV-P2-014 — working tree 범위 집계가 untracked 보호 shell을 제외

  • 심각도: Low
  • 상태: 수정 완료
  • 관련 요구사항: Goal 실행형 계획 유지보수 규칙, 리뷰 범위 전체 확인
  • 관련 계약: 없음
  • 소유 Task: R2.11, goal P2-R11

관찰 내용

P2-R8git diff HEAD --name-only의 37개 결과를 현재 working tree 전체 범위로 사용해 review를 종료했다. 하지만 새 src/app/protected-admin-shell.tsx는 untracked라 이 명령에 포함되지 않으며 실제 status에는 38개 변경 항목이 있다.

근거

  • 실행: git diff HEAD --name-only | wc -l은 37, git status --porcelain=v1 | wc -l은 38을 반환했다.
  • 실행: git ls-files --others --exclude-standardsrc/app/protected-admin-shell.tsx를 반환했다.
  • 문서: plan-task.md:954-960P2-R8 검증은 git diff HEAD --name-only만 사용하고 untracked 확인 명령이 없다.
  • 문서: 4차 반영 전 review 상단과 종료 판정은 37개를 현재 working tree 전체 범위로 기록했다.

재현 또는 검증 절차

  1. git diff HEAD --name-only | wc -l로 tracked diff를 센다.
  2. git status --porcelain=v1 | wc -lgit ls-files --others --exclude-standard를 실행한다.
  3. 실제 결과는 tracked 37개, untracked 1개, working tree 전체 38개 항목이다.
  4. 요구되는 결과는 review 범위와 대체 검증이 tracked·untracked를 구분해 전체 38개를 추적하는 것이다.

영향

코드 동작에는 직접 영향이 없지만 새 파일이 review 대상·diff 감사에서 빠질 수 있다. 문서 정합성 복구를 완료했다는 P2-R8의 검증 방법과 종료 판정 신뢰성이 다시 낮아진다.

권장 조치

문서 전용 P2-R11에서 과거 37개 tracked 결과는 보존하고 현재 범위를 tracked 37개 + untracked 1개로 명시한다. 후속 검증은 git status --short --untracked-files=all과 untracked 목록을 함께 사용한다.

판정 기록

  • 2026-07-27 — tracked diff, porcelain status와 untracked 목록의 수치 차이를 직접 확인해 문서 범위 누락으로 확정했다.
  • 2026-07-27 — P2-R11에서 현재 범위를 tracked 37개 + untracked 1개 = 38개로 정정하고 git status --short --untracked-files=all과 untracked 목록 검증을 누적해 수정 완료로 판정했다.

REV-P2-015 — 보호 route probe pending 중 root가 비어 진행 상태를 알리지 않음

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: PRD §10.5 loading·300ms 비동기 피드백, §10.7 비동기 상태 live region, P2-R10
  • 관련 계약: API Contract §1.5
  • 소유 Task: R2.12, goal P2-R12

관찰 내용

최초 보호 route probe 또는 오류 화면의 수동 retry가 pending이면 미검증 branch가 null을 반환한다. 350ms 이상 응답이 지연돼도 화면과 접근성 tree에 loading 안내가 없고, retry button을 누른 keyboard focus는 제거된 element에서 BODY로 이동한다.

근거

  • 코드: src/app/App.tsx:161-167은 현재 attempt가 미검증이고 error가 없으면 null을 반환한다.
  • 코드: src/app/App.tsx:171-173은 retry click에서 error를 먼저 지우고 retry key를 증가시켜 같은 빈 branch로 전환한다.
  • 테스트 공백: src/app/App.test.tsx:397-400은 retry pending의 shell·logout 비노출만 확인하고 visible status·loading feedback은 확인하지 않는다.
  • 요구사항: PRD §10.5는 첫 loading 상태와 300ms 이상 작업의 시각 피드백을, §10.7은 비동기 결과·오류의 적절한 live region을 요구한다.
  • 런타임: retry 성공 응답을 350ms 지연했을 때 #root child 0개, main·status·alert 0개, active element BODY를 Chromium에서 확인했다.

재현 또는 검증 절차

  1. ADMIN session의 probe를 404로 응답해 retry button을 표시한다.
  2. 다음 성공 응답을 350ms 이상 지연하고 retry button을 누른다.
  3. pending 동안 #root, landmark, role=status|alert와 active element를 확인한다.
  4. 실제 결과는 빈 root와 BODY focus다. 요구되는 결과는 보호 shell은 숨기되 RouteFrame 안의 지속적인 한국어 loading status를 제공하는 것이다.

영향

느린 네트워크에서 사용자는 retry가 시작됐는지, 앱이 멈췄는지 구분할 수 없다. 보조기기 사용자에게는 비동기 진행 상태가 전혀 전달되지 않으며 초기 보호 route 진입도 같은 빈 화면을 공유한다.

권장 조치

새 loading 시스템을 만들지 않고 기존 PageState loading variant를 미검증 branch에 조합한다. 최초 probe와 retry pending unit test, 350ms 지연 server boundary E2E로 visible status 1건과 보호 shell·logout 0건을 함께 고정한다.

판정 기록

  • 2026-07-27 — 코드 branch와 350ms Chromium 지연 재현을 PRD 비동기 상태 기준에 대조해 확정했다.
  • 2026-07-27 — P2-R12에서 미검증 branch에 기존 PageState loading status를 표시하고 App focused test와 server boundary E2E로 pending 중 status 1건, 보호 shell·logout 0건을 확인해 수정 완료로 판정했다.

REV-P2-016 — 고정 E2E script가 focused 필터와 network retry 증거 공백을 가림

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: P2-R7, P2-R10 완료 증거, Goal 실행형 계획 검증 규칙
  • 관련 계약: API Contract §1.5
  • 소유 Task: R2.13, goal P2-R13

관찰 내용

e2e·e2e:mock script가 mode별 모든 spec 경로를 명령 자체에 고정한다. 따라서 plan에 기록된 npm run e2e -- tests/e2e/server-mode-boundary.spec.ts는 focused 실행이 아니라 고정 4개 spec을 모두 실행한다. 이 32 tests 통과가 P2-R10의 server boundary 기대 16 tests 미달과 network retry E2E 부재를 가렸다.

근거

  • 설정: package.json:16-17은 server 4개, mock 2개 spec을 script 인수로 고정한다.
  • 실행: npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts는 4 files / 32 tests를 수집했다.
  • 대조 실행: VITE_API_MODE=server npx playwright test tests/e2e/server-mode-boundary.spec.ts --list는 1 file / 12 tests만 수집했다.
  • 계획: plan-task.mdP2-R10 기대 결과는 server boundary 4 projects / 16 tests 이상이다.
  • 테스트 공백: tests/e2e/server-mode-boundary.spec.ts:76-93은 최초 network error만 확인하고 retry button을 누르지 않는다. network retry 재실패는 src/app/App.test.tsx:405-430에만 있다.
  • 기록: P2-R10 RED/GREEN E2E는 focused 명령이 32 tests를 실행한 결과를 boundary E2E 증거로 사용했다.

재현 또는 검증 절차

  1. npm script를 통한 server boundary --list와 직접 Playwright boundary --list를 각각 실행한다.
  2. 전자는 4 files / 32 tests, 후자는 1 file / 12 tests임을 확인한다.
  3. boundary spec에서 network error test가 retry를 수행하는지 확인한다.
  4. 요구되는 결과는 bare mode allowlist를 유지하면서 CLI file filter는 1개 file만 수집하고, boundary spec이 network retry 재실패까지 포함해 16 tests 이상을 실행하는 것이다.

영향

focused 회귀 명령이 느리고 범위를 오해하게 하며, 무관한 spec 통과 수가 Task별 완료 기준 미달을 숨긴다. network retry의 실제 browser no-fallback·fail-closed 경계가 unit test에만 의존한다.

권장 조치

script에는 mode env와 playwright test만 두고 mode별 exact allowlist는 단일 playwright.config.tstestMatch로 이동해 CLI filter와 교집합으로 동작하게 한다. network error → retry → network 재실패 E2E를 추가하고 focused 16, bare server 36, bare mock 28 tests를 각각 검증한다.

판정 기록

  • 2026-07-27 — npm/direct list 결과와 boundary spec 시나리오 수를 P2-R10 기대 결과에 대조해 검증 증거 회귀로 확정했다.
  • 2026-07-27 — P2-R13에서 npm scripts를 bare playwright test로 되돌리고 mode별 allowlist를 playwright.config.ts testMatch로 이동했다. focused list 1 file / 16 tests, network retry 재실패 E2E 16 tests 통과를 확인해 수정 완료로 판정했다.

REV-P2-017 — P2-R12 test 증거가 실제 test 구조·실행 수와 불일치

  • 심각도: Low
  • 상태: 수정 완료
  • 관련 요구사항: Goal 실행형 계획의 완료 증거·Progress 정합성 규칙, 리뷰 종료 조건
  • 관련 계약: 없음
  • 소유 Task: R2.14, goal P2-R14

관찰 내용

P2-R12의 완료 체크는 최초 probe pending과 retry pending “두 test를 추가”했고 App test가 28건 이상이어야 한다고 기록한다. 실제 파일은 최초 pending 전용 test 1건을 추가하고 기존 404 retry test에 pending assertion을 보강한 27 tests 구조이며, 수정 검증 기록도 이를 “신규 pending status tests 2건”이라고 잘못 설명한다.

근거

  • 코드: src/app/App.test.tsxtest( 선언은 27건이다.
  • 코드: 최초 pending은 shows an accessible status while the initial protected route probe is pending 전용 test로 추가됐고, retry pending은 기존 retries a protected route 404 and reveals the shell only after the current retry succeeds test 안에 assertion이 추가됐다.
  • 실행: npm run test:run -- src/app/App.test.tsx는 1 file / 27 tests를 통과했다.
  • 계획: plan-task.mdP2-R12 TDD 절차는 두 test 추가를 완료로 체크하고 기대 결과를 App 28 tests 이상으로 둔다.
  • 기록: plan/review의 P2-R11~P2-R13 수정 검증은 “신규 pending status tests 2건”과 GREEN 27 tests를 동시에 기록한다.

재현 또는 검증 절차

  1. rg -c '^test\(' src/app/App.test.tsx를 실행해 27을 확인한다.
  2. rg -n 'initial protected route probe is pending|retries a protected route 404' src/app/App.test.tsx로 최초 전용 test 1건과 기존 retry test 보강 구조를 확인한다.
  3. P2-R12의 TDD 체크·기대 결과와 P2-R11~P2-R13 수정 검증의 RED/GREEN 기록을 대조한다.

영향

제품 동작과 접근성 feedback은 unit·E2E로 유지되지만, 완료하지 않은 수치 기준을 완료로 체크하고 test case와 검증 시나리오를 혼용해 후속 reviewer가 TDD 증거를 오해하게 된다.

권장 조치

중복 retry test를 추가하지 않는다. P2-R14 문서 전용 정정으로 실제 구조를 “최초 전용 test 1건 추가 + 기존 retry test 보강, App 27 tests”로 명시하고, 이전 잘못된 표현과 정정 사유를 새 Progress에 보존한 뒤 문서 diff·검색 검증을 누적한다.

판정 기록

  • 2026-07-27 — 현재 App test 선언 27건, test 이름·assertion 위치, focused 실행 27 tests와 plan/review의 28 tests·두 신규 test 주장을 직접 대조해 확정했다.
  • 2026-07-27 — P2-R14에서 plan/review의 P2-R12 완료 증거를 최초 pending 전용 test 1건 + 기존 404 retry test 보강 + App 27 tests로 정정했다. App focused test와 stale 표현 검색·문서 diff 검사를 통과해 수정 완료로 판정했다.
  • 2026-07-27 — 제품 동작은 server boundary 16 tests와 full unit 147 tests에서 유지됨을 확인해 심각도를 Low로 판정하고 문서 전용 P2-R14로 전환했다.

REV-P2-018 — plan 전환 절이 완료된 P2-R8~P2-R13을 미수정으로 표시

  • 심각도: Low
  • 상태: 수정 완료
  • 관련 요구사항: Goal 실행형 계획의 완료 증거·Progress 정합성 규칙, 리뷰 종료 조건
  • 관련 계약: 없음
  • 소유 Task: R2.15, goal P2-R15

관찰 내용

review 요약과 각 발견 상세, 종료 판정은 REV-P2-010REV-P2-016P2-R8P2-R13을 모두 수정 완료로 표시한다. 그러나 같은 문서의 §7은 3차 회귀 Task P2-R8P2-R10과 4차 회귀 Task P2-R11P2-R13을 각각 “아직 수정하지 않았다”고 현재형으로 표시한다.

근거

  • 문서: review §5의 REV-P2-010~REV-P2-016 상태는 모두 수정 완료다.
  • 문서: review §6의 각 판정 기록과 §9의 P2-R9, P2-R10, P2-R11~P2-R13 수정 검증은 실제 수정·검증 완료를 기록한다.
  • 문서: review §7은 3차·4차 확정 항목을 현재도 “아직 수정하지 않았다”고 표시한다.
  • 문서: review §8은 열린 확정 항목 없음과 코드·문서 정합성 충족을 결론으로 표시한다.

재현 또는 검증 절차

  1. rg -n '아직 수정하지 않았다|REV-P2-01[0-6]|P2-R(8|9|10|11|12|13)' docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md를 실행한다.
  2. §5·§6·§8의 수정 완료 상태와 §7의 미수정 표현 두 곳을 대조한다.
  3. §9의 과거 시점 “남은 항목”은 이력 보존 대상이고, §7은 현재 plan 전환 상태를 설명하는 절임을 구분한다.

영향

제품 동작과 검증 결과에는 영향이 없지만, Phase 3 실행자나 reviewer가 P2-R8~P2-R13을 다시 실행해야 하는 열린 Task로 오해할 수 있고 리뷰 종료 판정의 신뢰도가 낮아진다.

권장 조치

P2-R15 문서 전용 정정으로 review §7의 두 현재 상태 문장만 실제 수정 완료 상태로 바꾼다. §9의 당시 남은 항목과 과거 검증 기록은 삭제하지 않고, stale 문자열 검색과 문서 diff 검사를 누적한다.

판정 기록

  • 2026-07-27 — review §5·§6·§7·§8과 실제 P2-R8~P2-R13 수정 검증 기록을 대조해 현재 상태 모순 2건을 확인하고 Low로 확정했다.
  • 2026-07-27 — 제품 코드·test·mode 경계는 full unit 147, server E2E 36, mock E2E 28 tests에서 유지돼 문서 전용 P2-R15로 전환했다.
  • 2026-07-27 — P2-R15에서 review §7의 3차·4차 회귀 Task 설명을 수정 완료 상태로 정정했다. 과거 검증 기록은 보존하고 stale 현재 상태 검색과 문서 diff 검사를 통과해 수정 완료로 판정했다.

REV-P2-019 — Phase 2 현재 상태·최신 Progress가 P2-R15 완료와 불일치

  • 심각도: Low
  • 상태: 수정 완료
  • 관련 요구사항: Goal 실행형 계획의 현재 상태·Progress 누적 규칙, 리뷰 종료 조건
  • 관련 계약: 없음
  • 소유 Task: R2.16, goal P2-R16

관찰 내용

P2-R15 Task 본문과 review는 수정 완료 및 열린 확정 항목 없음을 표시한다. 그러나 plan의 Phase 2 상단 현재 상태는 최초 추가 당시 설명에 머물고, 하단 최신 Progress는 6차 재검증의 P2-R15 수정 필요 상태로 끝난다.

근거

  • 문서: plan Phase 2 상단 현재 상태는 완료 범위와 Phase 3 진행 가능 여부를 표시하지 않는다.
  • 문서: plan의 P2-R15 체크와 inline 수정 검증은 모두 완료됐다.
  • 문서: plan 하단 마지막 Progress는 P2-R15를 남은 항목으로 표시한다.
  • 문서: review §5·§6·§7·§8·§9는 REV-P2-018, P2-R15 수정 완료와 열린 항목 없음을 표시한다.

재현 또는 검증 절차

  1. Phase 2 완료 상태 exact 검색을 실행해 no match와 exit 1을 확인한다.
  2. tail -n 16 docs/20260725_AI캐릭터관리자웹/plan-task.md에서 마지막 Progress가 P2-R15 수정 필요 상태인지 확인한다.
  3. 같은 문서의 P2-R15 체크·inline 검증 및 review 종료 판정과 대조한다.

영향

제품 동작에는 영향이 없지만 Phase 3 실행자가 완료된 P2-R15를 열린 선행 작업으로 오해하거나 Phase 2 완료 여부를 다시 판정해야 한다.

권장 조치

P2-R16 문서 전용 정정으로 Phase 2 상단 현재 상태를 실제 완료 범위와 Phase 3 진행 가능 상태로 갱신하고, 6차 당시 남은 항목은 보존한 채 하단에 최신 수정 검증을 누적한다.

판정 기록

  • 2026-07-27 — Phase 2 상단 상태, P2-R15 Task·review 완료 상태와 하단 최신 Progress를 대조해 현재 상태 모순을 Low로 확정했다.
  • 2026-07-27 — 제품 코드·test·설정 변경이 없는 문서 정합성 문제이므로 문서 전용 P2-R16으로 전환했다.
  • 2026-07-27 — P2-R16에서 Phase 2 상단 현재 상태와 하단 최신 Progress를 실제 완료 상태로 정렬하고 문서 검색·전체 Phase 2 Gate를 통과해 수정 완료로 판정했다.

7. 확정 항목의 plan·goal 전환

1차 확정 5건은 기존 완료 체크를 되돌리지 않고 plan-task.md의 Phase 2에 다음 회귀 수정 Task로 추가했다.

Task R2.1 — 보호 route 검증 실패 시 fail-closed 복구

  • 연결 review: REV-P2-001
  • goal: P2-R1
  • 핵심 완료 증거: 404·network·새 session pending에서 보호 shell 비노출, shell 밖 오류 UI, App/server/auth E2E와 P2-GATE
  • 범위 밖: 인증/session 아키텍처 교체, Phase 3 구현

Task R2.2 — Mock Preview API origin·JWT 오류 계약 복구

  • 연결 review: REV-P2-002, REV-P2-003
  • goal: P2-R2
  • 핵심 완료 증거: exact API origin, invalid·revoked JWT 401, 정상 auth preview 회귀 없음, focused handler test·mock E2E·P2-GATE
  • 범위 밖: 계약 미제공 DTO·role 의미 추정과 도메인 fixture

Task R2.3 — Phase 번호 표시·구현 문서 정합성 복구

  • 연결 review: REV-P2-004, REV-P2-005
  • goal: P2-R3
  • 핵심 완료 증거: Phase 3 사용자 문구, 실제 Files, 하단 Phase 2 Progress, 문서/App focused test와 diff check
  • 범위 밖: Phase 3 기능 구현과 기존 완료 기록 삭제

2차 재검증의 새 확정 4건도 plan-task.mdR2.4~R2.7 회귀 Task로 전환했고 2026-07-27 수정·검증을 완료했다.

Task R2.4 — 동일 세션 재진입 fail-closed 복구

  • 연결 review: REV-P2-006
  • goal: P2-R4
  • 핵심 완료 증거: 최초 성공 뒤 동일 session 재진입 pending·404·network·403에서 보호 shell 비노출, focused App test·server boundary E2E·P2 Gate
  • 범위 밖: 인증/session 저장 방식 전면 교체, Phase 3 구현

Task R2.5 — Auth fixture status·media type 계약 복구

  • 연결 review: REV-P2-007
  • goal: P2-R5
  • 핵심 완료 증거: 비ADMIN logout 403, invalid·revoked 401 유지, non-JSON login 415, 정상 login/logout 회귀 없음
  • 범위 밖: 제공되지 않은 login credential 정책과 회원 role 의미 확장

Task R2.6 — 모든 mock route state의 지속 안내 복구

  • 연결 review: REV-P2-008
  • goal: P2-R6
  • 핵심 완료 증거: login·성공 shell·403·404/network에서 banner 표시, server mode 미표시, 접근성·320px 회귀 없음
  • 범위 밖: 오류 화면 디자인 개편

Task R2.7 — Mode별 bare E2E 실행 경계 복구

  • 연결 review: REV-P2-009
  • goal: P2-R7
  • 핵심 완료 증거: bare npm run e2enpm run e2e:mock이 각 mode 유효 spec만 실행해 exit 0, 목록·실행 contract test
  • 범위 밖: Playwright config 복제와 CI 전면 재구성

3차 재검증의 새 확정 4건은 plan-task.mdR2.8~R2.10 회귀 Task로 전환했으며 2026-07-27 수정·검증을 완료했다.

Task R2.8 — Phase 2 완료 문서 추적성 복구

  • 연결 review: REV-P2-010
  • goal: P2-R8
  • 핵심 완료 증거: Phase 2 Files, 완료 Task Files·Interfaces, review 범위·경로 수·종료 판정과 최신 검증 기록 일치
  • 범위 밖: 애플리케이션 코드·test·설정 변경과 기존 기록 삭제

Task R2.9 — Mock Preview 모바일 메뉴의 반응형·inert 경계 복구

  • 연결 review: REV-P2-011, REV-P2-012
  • goal: P2-R9
  • 핵심 완료 증거: menu open background에 banner 포함, lg 전환 시 menu·inert·aria-hidden 해제, hidden trigger focus 복귀 없음, App/mock/accessibility E2E와 P2 Gate
  • 범위 밖: Admin shell 전면 교체, Phase 3 UI와 신규 breakpoint 체계

Task R2.10 — 보호 route 오류의 fail-closed 재시도 복구

  • 연결 review: REV-P2-013
  • goal: P2-R10
  • 핵심 완료 증거: 404·network 오류의 수동 retry, 재시도 비성공 중 shell·logout 비노출, 성공한 현재 attempt 뒤 shell 표시, App/server boundary E2E와 P2 Gate
  • 범위 밖: 자동 retry·mock fallback, Phase 3 목록 오류 UI와 API client 전면 교체

4차 재검증의 새 확정 3건은 plan-task.mdR2.11~R2.13 회귀 Task로 전환했으며 2026-07-27 수정·검증을 완료했다.

Task R2.11 — Phase 2 working tree 전체 경로 집계 복구

  • 연결 review: REV-P2-014
  • goal: P2-R11
  • 핵심 완료 증거: tracked 37개 + untracked 1개 = working tree 38개 항목과 plan/review 현재 범위 일치, untracked 포함 검증 명령
  • 범위 밖: 애플리케이션 코드·test·설정 변경과 과거 tracked 실행 기록 삭제

Task R2.12 — 보호 route probe 대기 상태의 접근 가능한 피드백 복구

  • 연결 review: REV-P2-015
  • goal: P2-R12
  • 핵심 완료 증거: 최초·retry pending의 visible live status, 빈 root 0건, 보호 shell·logout 비노출, App/server boundary E2E와 P2 Gate
  • 범위 밖: 보호 shell skeleton 선노출, 자동 retry·mock fallback과 router 전면 교체

Task R2.13 — Mode별 focused E2E 필터와 network retry 증거 복구

  • 연결 review: REV-P2-016
  • goal: P2-R13
  • 핵심 완료 증거: bare mode allowlist 유지, CLI file filter 1 file 수집, network retry 재실패 E2E, focused·bare E2E와 P2 Gate
  • 범위 밖: Playwright config 복제와 project matrix·CI 전면 재구성

5차 재검증의 새 확정 Low 1건은 plan-task.mdR2.14 회귀 Task로 전환했으며 2026-07-27 수정·검증을 완료했다.

Task R2.14 — P2-R12 test 증거 정합성 복구

  • 연결 review: REV-P2-017
  • goal: P2-R14
  • 핵심 완료 증거: 최초 pending 전용 test 1건 + 기존 retry test 보강 + App 27 tests라는 실제 구조와 plan/review 기록 일치, 문서 diff·검색 검증
  • 범위 밖: 애플리케이션 코드·test·설정 변경, 중복 retry test 추가와 기존 실행 이력 삭제

6차 재검증의 새 확정 Low 1건은 plan-task.mdR2.15 회귀 Task로 전환했으며 2026-07-27 수정·검증을 완료했다.

Task R2.15 — review plan 전환 절의 현재 수정 상태 정합성 복구

  • 연결 review: REV-P2-018
  • goal: P2-R15
  • 핵심 완료 증거: review §7의 P2-R8~P2-R13 상태가 §5·§6·§8과 일치, 과거 이력 보존, stale 현재 상태 검색·문서 diff 검사
  • 범위 밖: 애플리케이션 코드·test·설정 변경, 과거 남은 항목과 검증 기록 삭제

7차 재검증의 새 확정 Low 1건은 plan-task.mdR2.16 회귀 Task로 전환했으며 2026-07-27 수정·검증을 완료했다.

Task R2.16 — Phase 2 현재 상태·하단 Progress 정합성 복구

  • 연결 review: REV-P2-019
  • goal: P2-R16
  • 핵심 완료 증거: Phase 2 상단 완료 상태, 과거 6차 기록 보존, 최신 Progress의 P2-R15·P2-R16 완료와 Phase 3 진행 가능 판정 일치
  • 범위 밖: 애플리케이션 코드·test·설정 변경, 과거 기록 삭제와 Phase 3 구현

create_goal objective 초안

[P2-R1|P2-R2|P2-R3]의 연결된 확정 review 항목을 수정하고 회귀를 방지한다.
plan-task.md에 추가된 해당 회귀 수정 Task 하나만 수행한다.
실패 재현, 최소 수정, focused test, 관련 E2E, P2-GATE와 누적 기록이 모두 끝나기 전에는 complete로 표시하지 않는다.
관련 없는 리팩터링, Phase 3 기능 구현과 계약 추정은 범위 밖이다.

2차 초안 objective:

[P2-R4|P2-R5|P2-R6|P2-R7]의 연결 review 항목 하나를 수정하고 회귀를 방지한다.
plan-task.md에 신규 회귀 Task로 전환된 해당 범위만 수행한다.
실패 재현, 최소 수정, focused test, 관련 mode E2E와 P2-GATE 기록이 끝나기 전에는 complete로 표시하지 않는다.
Phase 3 기능, 계약 추정과 관련 없는 리팩터링은 범위 밖이다.

3차 초안 objective:

[P2-R8|P2-R9|P2-R10]의 연결 review 항목을 수정하고 Phase 2 완료 문서, mobile menu 접근성 회귀 또는 보호 route 오류 복구를 완료한다.
plan-task.md에 신규 회귀 Task로 전환된 해당 범위만 수행한다.
TDD 예외 대체 검증 또는 RED/GREEN/REFACTOR, focused test, 관련 mode E2E와 P2-GATE 기록이 끝나기 전에는 complete로 표시하지 않는다.
Phase 3 기능, 기존 기록 삭제와 Admin shell 전면 재구성은 범위 밖이다.

4차 초안 objective:

[P2-R11|P2-R12|P2-R13]의 연결 review 항목 하나를 수정하고 Phase 2 working tree 추적성, 보호 route pending 피드백 또는 focused E2E 증거를 복구한다.
plan-task.md에 신규 회귀 Task로 전환된 해당 범위만 수행한다.
TDD 예외 대체 검증 또는 RED/GREEN/REFACTOR, focused test, 관련 mode E2E와 P2-GATE 기록이 끝나기 전에는 complete로 표시하지 않는다.
Phase 3 기능, 자동 fallback과 Playwright config 복제·CI 전면 재구성은 범위 밖이다.

5차 초안 objective:

P2-R14의 연결 review 항목을 수정하고 P2-R12 test 증거의 문서 정합성을 복구한다.
plan-task.md에 신규 회귀 Task로 전환된 문서 정정 범위만 수행한다.
실제 App test 구조·focused 결과 대조, plan/review 정정 기록, 문서 검색·diff 검증이 끝나기 전에는 complete로 표시하지 않는다.
애플리케이션 코드·test·설정 변경, 중복 test 추가와 기존 실행 이력 삭제는 범위 밖이다.

6차 초안 objective:

P2-R15의 연결 review 항목을 수정하고 review plan 전환 절의 현재 상태 정합성을 복구한다.
plan-task.md에 신규 회귀 Task로 전환된 문서 정정 범위만 수행한다.
review §5·§6·§7·§8 대조, 과거 이력 보존, stale 문자열 검색과 문서 diff 검증이 끝나기 전에는 complete로 표시하지 않는다.
애플리케이션 코드·test·설정 변경과 과거 검증 기록 삭제는 범위 밖이다.

7차 초안 objective:

P2-R16의 연결 review 항목을 수정하고 Phase 2 현재 상태와 하단 최신 Progress의 정합성을 복구한다.
plan-task.md에 신규 회귀 Task로 전환된 문서 정정 범위만 수행한다.
완료 상태 exact 검색, 최신 Progress 대조, 과거 이력 보존, 문서 diff와 Phase 2 Gate 검증이 끝나기 전에는 complete로 표시하지 않는다.
애플리케이션 코드·test·설정 변경과 Phase 3 구현은 범위 밖이다.

8. 리뷰 종료 판정

판정 항목 결과 근거
리뷰 범위 전체 확인 충족 Phase 2 tracked diff 37개 + untracked 1개 = working tree 38개 항목, 관련 문서·unit·E2E·build와 mock browser runtime 경계를 대조
후보 항목 판정 완료 충족 1~7차 확정 19건 모두 수정 완료, 후보·오탐·보류 0건
확정 항목 plan 반영 충족 REV-P2-019P2-R16 회귀 Task로 전환하고 수정 완료함
보류 항목의 담당·재개 조건 기록 해당 없음 보류 항목 없음
검증 명령과 결과 기록 충족 focused unit, focused server/mock list, server boundary 16, mock preview 24, typecheck/lint, working tree 집계와 문서 diff 검사를 기록
Blocker·High 확정 이슈 0건 충족 REV-P2-006, REV-P2-007 수정 완료
Medium 확정 이슈 0건 충족 REV-P2-015~016 수정 완료
Low 확정 이슈 0건 충족 REV-P2-019, P2-R16 수정 완료
코드와 문서 정합성 충족 Phase 2 상단 현재 상태·하단 최신 Progress와 review 종료 판정 일치

최종 결론: Phase 2 구현·review·plan 정합성 충족, Phase 3 진행 가능

남은 항목: 현재 review의 열린 확정 항목 없음. mock 통과는 Phase 3의 실제 server integration 완료로 간주하지 않는다.

9. 수정 후 검증 기록

기존 기록을 삭제하거나 덮어쓰지 않고 차수별로 누적한다.

수정 검증 미수행 — 2026-07-27

  • 무엇을: Phase 2 구현과 기존 test·설정은 수정하지 않고, 확정 5건의 review 문서와 P2-R1~P2-R3 회귀 Task만 작성했다.
  • 왜: 이번 요청은 코드 리뷰·검증·기록 범위이며 확정 문제의 구현 수정은 별도 goal로 분리해야 한다.
  • 어떻게:
    • 수정 전 기준 검증은 4절의 unit, typecheck, lint, build, production guard, mock/server E2E와 handler 런타임 재현으로 완료했다.
    • 수정 후 focused test와 P2-GATE는 아직 실행 대상이 아니다.
  • 남은 항목: REV-P2-001~005, P2-R1~P2-R3 실행.

P2-R1~P2-R3 수정 검증 — 2026-07-27

  • 무엇을: REV-P2-001~005P2-R1~P2-R3 범위에서 수정했다. 보호 route 404·network error는 shell 밖 오류 UI로 fail-closed 처리하고, mock handler는 exact API origin에만 매칭하며 invalid·revoked JWT logout은 401로 반환한다. Phase 2 placeholder 문구와 Files·Progress 기록도 실제 구현에 맞췄다.
  • 왜: Phase 2 mock/server 경계가 실제 API 계약과 Phase 1 보호 route 경계를 우회하지 않게 하고, 완료 기록이 재현 가능한 파일·명령과 일치하게 하기 위해서다.
  • 어떻게:
    • RED: npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts — App 2건, auth handler 2건이 기대대로 실패했다.
    • RED: npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/mock-preview-docs.test.ts — Phase 3 placeholder와 Phase 2 Files·Progress 기록 불일치로 3 tests가 기대대로 실패했다.
    • GREEN focused: npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts src/shared/mocks/browser.test.ts — 3 files / 27 tests 통과.
    • GREEN docs/App: npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/mock-preview-docs.test.ts — 2 files / 21 tests 통과.
    • P2 focused Gate: npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx — 7 files / 23 tests 통과.
    • Surface: npm run e2e -- tests/e2e/server-mode-boundary.spec.ts — 4 projects / 12 tests 통과. npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts — 4 projects / 20 tests 통과. npm run e2e -- tests/e2e/auth.spec.ts tests/e2e/accessibility-shell.spec.ts — 4 projects / 16 tests 통과.
    • Final Gate: npm run test:run — 34 files / 135 tests 통과. npm run typecheck, npm run lint, npm run build:dev, npm run build:prod, git diff --check — 모두 성공.
    • Production guard: VITE_API_MODE=mock npm run build:prodVITE_API_MODE=mock is only available during development로 기대대로 거부.
    • LSP diagnostics: src/app directory 3 files / 0 diagnostics, src/shared/mocks directory 8 files / 0 diagnostics. App.tsx 단일 fresh diagnostics는 timeout이었고 directory diagnostics와 typecheck로 보완했다.
  • 남은 항목: 현재 리뷰의 열린 Blocker·High·Low 항목 없음. Phase 3 이후 server integration은 별도 Phase 증거로 기록한다.

Reviewer gate 보강 — 2026-07-27

  • 무엇을: reviewer gate에서 같은 ADMIN token을 재사용하는 logout → login 뒤 probe 실패 시 이전 verifiedProtectedRouteToken이 남아 보호 shell을 열 수 있다는 P2-R1 blocker를 확인하고 수정했다.
  • 왜: mock preview는 logout 후 재login 때 같은 mock-admin-jwt를 재활성화하므로, fresh session 테스트만으로는 현재 session의 probe 성공 여부를 충분히 증명하지 못하기 때문이다.
  • 어떻게:
    • RED: npm run test:run -- src/app/App.test.tsxclears a previous protected route verification before reusing the same token after login 1건이 보호 shell main 노출로 기대대로 실패했다.
    • GREEN: 같은 command — 1 file / 19 tests 통과. session이 null이 되면 route error와 verified token을 초기화하도록 수정했다.
    • Focused regression: npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts src/shared/mocks/browser.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts — 4 files / 31 tests 통과.
    • Surface regression: npm run e2e -- tests/e2e/server-mode-boundary.spec.ts — 4 projects / 12 tests 통과. npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts — 4 projects / 20 tests 통과.
  • Reviewer 재검토: 동일 reviewer가 delta를 재검토해 PASS를 반환했다. 세션 객체 기반 검증과 세션별 오류 귀속이 동일 token 재로그인 경로를 차단하고, 신규 blocker 없음으로 판정했다.
  • 남은 항목: reviewer gate blocker 없음. 최종 npm run test:run — 34 files / 136 tests 통과, npm run typecheck, npm run lint, npm run build:dev, npm run build:prod, git diff --check 성공, VITE_API_MODE=mock npm run build:prod production guard 거부를 확인했다.

2차 독립 재검증 — 2026-07-27

  • 무엇을: P2-R1~P2-R3 수정이 반영된 현재 working tree를 다시 검토해 REV-P2-006~009 High 2건·Medium 2건을 확정했다. 기존 REV-P2-001~005의 상세 상태는 1차 수정 검증 기록에 맞춰 수정 완료로 정정했다.
  • 왜: 기존 reviewer gate가 logout → 새 session 재로그인과 지정 spec Gate만 다뤘기 때문에, 동일 session route 재진입·mock 오류 화면·auth media/status·bare mode 명령 경계가 검증되지 않았다.
  • 어떻게:
    • Focused: npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx — 7 files / 23 tests 통과.
    • Full unit: npm run test:run — 34 files / 136 tests 통과.
    • Static/build: npm run typecheck, npm run lint, npm run build:dev, npm run build:prod, git diff --check HEAD — 모두 exit 0.
    • Production boundary: VITE_API_MODE=mock npm run build:prod — 기대대로 exit 1 거부. worker 파일과 mock bootstrap 문자열은 production 산출물에서 0건.
    • Filtered mock E2E: npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts tests/e2e/mock-mode-boundary.spec.ts — 4 projects / 24 tests 통과.
    • Filtered server regression: npm run e2e -- tests/e2e/server-mode-boundary.spec.ts tests/e2e/smoke.spec.ts tests/e2e/auth.spec.ts tests/e2e/accessibility-shell.spec.ts — 4 projects / 32 tests 통과.
    • Bare mock E2E: npm run e2e:mock — 56 tests 중 36 통과 / 20 실패. 두 mode의 --list는 동일한 6 files / 56 tests를 수집했다.
    • Runtime: 동일 session 재진입 404에서 보호 main·logout 각 1건, mock 403 화면 banner 0건, 비ADMIN logout 401, text/plain login 200을 확인했다.
  • 변경 범위: 이 review 문서만 누적 갱신했다. 코드·test·설정·plan-task.md는 변경하지 않았다.
  • 남은 항목: REV-P2-006~009P2-R4~P2-R7로 전환하고 별도 수정 goal에서 처리해야 한다.

P2-R4~P2-R7 수정 검증 — 2026-07-27

  • 무엇을: REV-P2-006~009plan-task.mdR2.4~R2.7로 전환하고 수정했다. 동일 session route 재진입은 visit key로 fail-closed 처리하고, auth fixture는 비ADMIN logout 403·unsupported media type 415를 반환한다. Mock Preview banner는 모든 mock route state의 공통 frame에 배치했고, bare E2E script는 mode별 유효 spec만 실행한다.
  • 왜: Phase 2 보호 route와 mock/server 경계가 성공 path뿐 아니라 재진입·오류·표준 실행 명령에서도 같은 계약을 유지해야 하기 때문이다.
  • 어떻게:
    • RED: npm run test:run -- src/app/App.test.tsx — 동일 session 재진입 404에서 보호 main이 남아 1 test가 실패했다.
    • RED: npm run test:run -- src/shared/mocks/__tests__/auth-handlers.test.ts — 비ADMIN logout 401과 text/plain login 200으로 2 tests가 실패했다.
    • RED: npm run test:run -- src/app/App.test.tsx — mock 403·보호 오류 화면의 Mock Preview status 부재로 2 tests가 실패했다.
    • RED: npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts — bare E2E script가 mode별 spec 경계를 명시하지 않아 1 test가 실패했다.
    • GREEN focused: npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts — 4 files / 37 tests 통과.
    • Full unit/static/build: npm run test:run — 34 files / 141 tests 통과. npm run typecheck, npm run lint, npm run build:dev, npm run build:prod 모두 exit 0.
    • E2E list: npm run e2e -- --list — server 4 files / 32 tests, npm run e2e:mock -- --list — mock 2 files / 24 tests.
    • Bare E2E: npm run e2e — 32 tests 통과, npm run e2e:mock — 24 tests 통과.
    • Production/diff: VITE_API_MODE=mock npm run build:prod — 기대대로 exit 1, test ! -e dist/mockServiceWorker.js, git diff --check HEAD — exit 0.
    • LSP/size: src/app 4 files / 0 diagnostics, src/shared/mocks 8 files / 0 diagnostics. App.tsxProtectedAdminShell 분리 후 153 pure LOC, 새 shell은 131 pure LOC다.
  • 남은 항목: reviewer gate blocker 없음. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.

3차 독립 재검증 — 2026-07-27

  • 무엇을: P2-R4~P2-R7 수정 뒤 Phase 2 문서 추적성, actual Service Worker 응답, mobile menu open 상태의 background inert·desktop breakpoint 전환과 보호 route 오류 recovery를 재검토했다.
  • 왜: 고정 viewport와 개별 banner/menu·오류 alert test만으로는 공통 RouteFrame 이동 뒤의 accessibility composition, responsive state 전환과 사용자의 fail-closed 복구를 증명하지 못하기 때문이다.
  • 어떻게:
    • npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx — 7 files / 25 tests 통과.
    • npm run test:run — 34 files / 141 tests 통과.
    • npm run typecheck, npm run lint, npm run build:dev, npm run build:prod — 모두 exit 0.
    • VITE_API_MODE=mock npm run build:prod — 기대대로 exit 1, production worker 파일·bootstrap 문자열 0건.
    • npm run e2e — 4 projects / 32 tests 통과. npm run e2e:mock — 4 projects / 24 tests 통과.
    • Chromium one-off — login·AI Character probe 응답 fromServiceWorker=true; 320px menu open에서 banner inert 귀속 없음, 1,200px 전환 뒤 overlay display:none과 main inert·aria-hidden=true 잔존을 확인.
    • 코드·test 대조 — ProtectedRouteErrorPage의 interactive recovery control 0건과 App/server boundary의 retry test 0건을 확인.
    • git diff HEAD --name-only | wc -l — 37개 경로, git diff --check HEAD — exit 0.
    • 문서 반영 검증 — npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts는 1 file / 3 tests 통과, review 상세 ID 13개·Phase 2 회귀 Task 10개를 확인했고 stale 3차 범위 문자열과 문서 whitespace 오류는 0건.
  • 변경 범위: 이 review 문서와 plan-task.mdREV-P2-010~013, P2-R8~P2-R10 판정·후속 계획만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
  • 남은 항목: P2-R8~P2-R10을 별도 goal로 실행하고 수정 후 검증 기록을 누적해야 한다.

P2-R9 수정 검증 — 2026-07-27

  • 무엇을: REV-P2-011, REV-P2-012P2-R9 범위에서 수정했다. Mock Preview 성공 shell의 banner를 mobile menu background inert container 안으로 이동하고, lg=1024px viewport match에서 mobile menu state를 닫아 hidden overlay와 inert shell 잔존을 제거했다.
  • 왜: mobile overlay가 열렸을 때 background banner가 보조기기 탐색에 남지 않아야 하며, responsive 전환 뒤 보이는 desktop shell이 pointer·keyboard·screen reader에서 즉시 사용 가능해야 하기 때문이다.
  • 어떻게:
    • RED unit: npm run test:run -- src/app/App.test.tsx src/shared/ui/__tests__/mock-mode-banner.test.tsx — 신규 App tests 2건이 기대대로 실패했다. 실패 핵심은 mock banner inert background not found모바일 주 메뉴 잔존이다.
    • RED e2e: npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts — 4 browser projects에서 신규 mock shell test가 모두 기대대로 실패했다. 실패 핵심은 open state의 bannerInert=false, bannerHidden=false다.
    • GREEN unit: 같은 focused unit command — 2 files / 26 tests 통과.
    • GREEN e2e: 같은 mock e2e command — 28 tests 통과. 320px open state의 banner/main inert 경계와 1,024px·1,200px desktop 전환 뒤 desktop navigation·logout 표시를 확인했다.
    • Regression: npm run e2e -- tests/e2e/accessibility-shell.spec.ts — 32 tests 통과. npm run typecheck, npm run lint — 모두 exit 0.
    • LSP diagnostics: src/app/App.tsx, src/app/protected-admin-shell.tsx, src/app/App.test.tsx, tests/e2e/mock-preview-shell.spec.ts 모두 0 diagnostics.
    • P2 focused Gate: npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx — P2-R9 문서 기록 반영 전에는 docs contract 1 test가 실패했다. 기록 반영 후 재실행해 7 files / 25 tests 통과했다.
  • 남은 항목: REV-P2-013, P2-R10 보호 route 오류 retry 복구.

P2-R10 수정 검증 — 2026-07-27

  • 무엇을: REV-P2-013P2-R10 범위에서 수정했다. 보호 route 오류 page에 native retry button을 추가하고 현재 session·route visit·retry attempt에 귀속된 결과만 사용해 성공 전 shell을 fail-closed로 유지했다. 정상 ApiError의 서버 message와 network 오류의 안전한 공통 안내를 구분했다.
  • 왜: 일시적인 404·network 오류가 복구된 뒤 browser refresh 없이 재진입할 수 있어야 하고, stale 또는 실패한 retry가 보호 shell을 열어서는 안 되기 때문이다.
  • 어떻게:
    • RED unit: npm run test:run -- src/app/App.test.tsx — 1 file / 26 tests 중 신규 2 tests가 보호 route 다시 시도 button 부재로 기대대로 실패했고 24 tests는 통과했다.
    • RED E2E: npm run e2e -- tests/e2e/server-mode-boundary.spec.ts — 32 tests 중 신규 retry 시나리오가 4 browser project에서 button 부재로 기대대로 실패했고 28 tests는 통과했다.
    • GREEN unit: 같은 App command — 1 file / 26 tests 통과. 404 없습니다. 보존, network 재실패의 공통 안내·retry 유지, pending 중 shell·logout 비노출과 성공 뒤 shell 표시를 확인했다.
    • GREEN E2E: 같은 server command — 4 projects / 32 tests 통과. 명시적 retry 전후 browser MSW worker 0건과 성공 전 shell·logout 0건을 확인했다.
    • Regression: npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx — 7 files / 25 tests 통과. npm run test:run — 34 files / 145 tests 통과.
    • Static/build: npm run typecheck, npm run lint, npm run build:dev, npm run build:prod — 모두 exit 0. no-excuse 검사도 변경 TS/TSX 3 files / 위반 0건이었다.
    • LSP diagnostics: src/app 4 files, tests/e2e 6 files에서 diagnostics 0건.
    • 수동·시각 확인: server mode 실제 브라우저 375px·768px·1,280px에서 button 높이 44px, :focus-visible=true, 한국어 clipping·비정상 줄바꿈 0건을 확인했다. retry 성공 뒤 shell·logout 표시와 mock worker 0건을 확인했고 기능 무결성·CJK 정밀 검토가 모두 PASS였다.
    • Diff: git diff --check -- src/app/App.tsx src/app/App.test.tsx tests/e2e/server-mode-boundary.spec.ts docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md — exit 0.
  • 남은 항목: 현재 리뷰의 열린 확정 항목 없음. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.

4차 독립 재검증 — 2026-07-27

  • 무엇을: P2-R8~P2-R10 반영 뒤 working tree 전체 집계, 보호 route 최초·retry pending 상태와 mode별 focused E2E 실행 증거를 재검토했다.
  • 왜: 통과한 자동화가 untracked 파일을 포함한 전체 범위, 300ms 이상 비동기 피드백과 Task가 지정한 단일 E2E spec 실행을 실제로 증명하는지 확인하기 위해서다.
  • 어떻게:
    • Focused unit: npm run test:run -- src/app/App.test.tsx src/shared/ui/__tests__/mock-mode-banner.test.tsx — 2 files / 28 tests 통과.
    • Full unit/static/build: npm run test:run — 34 files / 145 tests 통과. npm run typecheck, npm run lint, npm run build:dev, npm run build:prod — 모두 exit 0, build는 각 160 modules 변환.
    • Production boundary: VITE_API_MODE=mock npm run build:prod — 기대대로 exit 1. test ! -e dist/mockServiceWorker.js — exit 0, production JS mock bootstrap 문자열 검색 — 기대한 no-match exit 1.
    • Bare E2E: npm run e2e — 4 projects / 32 tests 통과. npm run e2e:mock — 4 projects / 28 tests 통과.
    • Working tree: git diff HEAD --name-only | wc -l — tracked 37개, git status --porcelain=v1 | wc -l — 전체 38개, untracked src/app/protected-admin-shell.tsx 1개 확인.
    • Focused list 대조: npm script 경유 server boundary list는 4 files / 32 tests, 직접 Playwright list는 1 file / 12 tests. P2-R10 기대 16 tests와 network retry E2E가 충족되지 않았다.
    • Chromium one-off: local listen·browser sandbox 실패 뒤 권한 허용 재실행. 404 retry 성공 응답을 350ms 지연했을 때 #root child, main, status, alert가 모두 0개이고 active element가 BODY였으며 응답 뒤 shell·logout은 표시됐다.
    • 문서 반영 검증: npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts — 1 file / 3 tests 통과. review 상세 ID 16개, Phase 2 회귀 Task 13개와 문서 whitespace 오류 0건 확인.
  • 변경 범위: 이 review 문서와 plan-task.mdREV-P2-014~016, P2-R11~P2-R13 판정·후속 계획만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
  • 남은 항목: P2-R11~P2-R13을 별도 goal로 실행하고 수정 후 검증 기록을 누적해야 한다.

P2-R11~P2-R13 수정 검증 — 2026-07-27

  • 무엇을: REV-P2-014~016P2-R11~P2-R13 범위에서 수정했다. working tree 범위는 tracked 37개 + untracked src/app/protected-admin-shell.tsx 1개로 정정했고, 보호 route pending에는 기존 PageState loading status를 표시했다. E2E script의 spec allowlist는 playwright.config.ts testMatch로 옮기고 network retry 재실패 E2E를 추가했다.
  • 왜: 완료 증거가 untracked 파일, 접근 가능한 300ms 이상 pending feedback, focused E2E file filter와 실제 network retry 경계를 빠뜨리지 않게 하기 위해서다.
  • 어떻게:
    • P2-R11 대체 검증: git diff HEAD --name-only | wc -l — tracked 37개, git status --short --untracked-files=all | wc -l — 전체 38개, git ls-files --others --exclude-standardsrc/app/protected-admin-shell.tsx 1개.
    • P2-R12 RED: npm run test:run -- src/app/App.test.tsx — 최초 pending 전용 test 1건과 기존 404 retry test의 pending assertion이 role="status" 부재로 기대대로 실패했다.
    • P2-R12 GREEN: 같은 command — 1 file / 27 tests 통과. 최초 probe와 retry pending 중 role="status", 보호 main·logout 0건을 확인했다.
    • P2-R13 RED: npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts — npm script와 Playwright config contract 2 tests가 기대대로 실패했다.
    • P2-R13 GREEN focused: npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts — 2 files / 6 tests 통과. npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts — 1 file / 16 tests, npm run e2e:mock -- --list tests/e2e/mock-preview-shell.spec.ts — 1 file / 24 tests.
    • Surface: npm run e2e -- tests/e2e/server-mode-boundary.spec.ts — 16 tests 통과. npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts — 24 tests 통과.
    • LSP diagnostics: src/app/App.tsx, src/app/App.test.tsx, playwright.config.ts, src/shared/mocks/__tests__/mode-boundary.test.ts 0 diagnostics. tests/e2e/server-mode-boundary.spec.ts 단일 fresh diagnostics는 timeout이었고 focused E2E와 typecheck로 보완한다.
    • Final Gate: npm run test:run — 34 files / 147 tests 통과. npm run typecheck, npm run lint, npm run build:dev, npm run build:prod — 모두 exit 0. npm run e2e — 36 tests 통과, npm run e2e:mock — 28 tests 통과.
    • Diff: git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md tests/e2e/server-mode-boundary.spec.ts package.json playwright.config.ts README.md docs/agent-guide/scripts.md src/app/App.tsx src/app/App.test.tsx src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts — exit 0.
  • 남은 항목: Phase 2 review의 열린 확정 항목 없음. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.

5차 독립 재검증 — 2026-07-27

  • 무엇을: P2-R11~P2-R13 반영 뒤 구현·test·plan·review의 완료 증거를 다시 대조해 REV-P2-017 Low 1건을 확정하고 P2-R14로 전환했다.
  • 왜: 자동 검증 통과뿐 아니라 완료 체크의 test case 수·구조와 review의 현재 수정 상태가 실제 결과와 일치하는지 확인하기 위해서다.
  • 어떻게:
    • Focused unit: npm run test:run -- src/app/App.test.tsx — 1 file / 27 tests 통과. rg -c '^test\(' src/app/App.test.tsx — 27건. 최초 pending 전용 test 1건과 기존 retry test의 pending assertion 보강을 확인했다.
    • Mode/docs unit: npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts — 2 files / 6 tests 통과.
    • Full unit/static/build: npm run test:run — 34 files / 147 tests 통과. npm run typecheck, npm run lint, npm run build:dev, npm run build:prod — 모두 exit 0, build는 각각 160 modules 변환.
    • Production boundary: VITE_API_MODE=mock npm run build:prod — 기대대로 exit 1과 VITE_API_MODE=mock is only available during development 확인. production 산출물의 worker 파일·mock bootstrap 문자열은 0건.
    • Focused list/E2E: server boundary list 1 file / 16 tests, mock preview list 1 file / 24 tests. 최초 sandbox listen EPERM 뒤 허용된 로컬 실행으로 server boundary 16 tests, mock preview 24 tests 통과.
    • Bare E2E: npm run e2e — 36 tests, npm run e2e:mock — 28 tests 통과.
    • Working tree: tracked diff 37개, untracked src/app/protected-admin-shell.tsx 1개, 전체 status 38개 항목을 재확인했다.
    • 문서 대조: P2-R12의 “두 test 추가”·App 28 tests 이상 완료 체크와 실제 27 tests 구조가 불일치함을 확인했다.
  • 변경 범위: 이 review 문서와 plan-task.mdREV-P2-017, P2-R14 판정·후속 계획만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
  • 남은 항목: P2-R14에서 실제 test 구조·개수를 plan/review에 정정하고 문서 검색·diff 검증을 누적해야 한다.

P2-R14 수정 검증 — 2026-07-27

  • 무엇을: REV-P2-017P2-R14 범위에서 수정했다. P2-R12 완료 증거를 실제 App test 구조인 최초 pending 전용 test 1건 + 기존 404 retry test의 pending assertion 보강 + App 27 tests로 정정했다.
  • 왜: 완료 문서가 test case 수와 검증 시나리오 수를 혼동하면 후속 reviewer가 P2-R12의 실제 회귀 고정 범위를 잘못 판단할 수 있기 때문이다.
  • 어떻게:
    • 대체 RED: rg -c '^test\(' src/app/App.test.tsx — 27건. rg -n 'initial protected route probe is pending|retries a protected route 404' src/app/App.test.tsx — 최초 pending 전용 test 1건과 기존 404 retry test 1건을 확인했다.
    • GREEN docs: plan/review의 P2-R12 TDD 절차·기대 결과·수정 검증 기록과 종료 판정을 실제 test 구조와 일치시켰다.
    • Focused: npm run test:run -- src/app/App.test.tsx — 1 file / 27 tests 통과.
    • 문서 검증: stale 신규 pending status tests 2건 검색 결과 0건, git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md — exit 0.
  • 남은 항목: Phase 2 review의 열린 확정 항목 없음. 애플리케이션 코드·test·설정은 변경하지 않았다.

6차 독립 재검증 — 2026-07-27

  • 무엇을: P2-R14 반영 뒤 구현·test·production·mode별 E2E와 review 현재 상태를 대조해 REV-P2-018 Low 1건을 확정했다.
  • 왜: 완료된 P2-R8~P2-R13의 상태가 review 모든 현재 상태 절에서 같은 의미로 표시되는지 확인하기 위해서다.
  • 어떻게:
    • npm run test:run — 34 files / 147 tests 통과.
    • npm run typecheck, npm run lint, npm run build:dev, npm run build:prod — 모두 exit 0.
    • VITE_API_MODE=mock npm run build:prod — 기대대로 exit 1 거부.
    • sandbox listen EPERM 후 로컬 실행 권한으로 npm run e2e 36 tests, npm run e2e:mock 28 tests 통과.
    • Chromium one-off — mock API 응답 fromServiceWorker=true, 320px mobile menu·logout 60px, menu link 선택 뒤 menu·inert 잔존 0건.
    • 문서 대조 — review §7의 stale “아직 수정하지 않았다” 현재 상태 2건과 §5·§6·§8의 수정 완료 상태 모순을 확인했다.
    • 문서 반영 후 npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts — 1 file / 3 tests 통과, plan/review 대상 git diff --check — exit 0.
  • 변경 범위: 이 review 문서와 plan-task.mdREV-P2-018, P2-R15 판정·후속 계획만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
  • 남은 항목: P2-R15에서 review §7 현재 상태를 정정하고 stale 문자열 검색·문서 diff 검증을 누적해야 한다.

P2-R15 수정 검증 — 2026-07-27

  • 무엇을: REV-P2-018P2-R15 범위에서 수정했다. review §7의 P2-R8P2-R10, P2-R11P2-R13 전환 설명을 현재 수정 완료 상태로 정정했다.
  • 왜: review 요약·상세·종료 판정은 이미 수정 완료로 표시하는데 plan 전환 절만 미수정 현재형으로 남아 Phase 3 실행자가 열린 Task로 오해할 수 있었기 때문이다.
  • 어떻게:
    • 대체 RED: review §7 stale 현재 상태 검색 — 2건이 기존 stale 현재 상태로 확인됐다.
    • GREEN docs: review §7의 두 문장을 수정 완료 상태로 정정하고, REV-P2-018 상태·발견 요약·종료 판정을 수정 완료로 맞췄다. §9의 당시 남은 항목과 과거 검증 기록은 삭제하지 않았다.
    • 검증: review §7 stale 현재 상태 검색 — no matches. git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md — exit 0.
  • 남은 항목: 현재 review의 열린 확정 항목 없음. 애플리케이션 코드·test·설정은 변경하지 않았다.

7차 독립 재검증 — 2026-07-27

  • 무엇을: P2-R15 반영 뒤 Phase 2 상단 현재 상태, Task 본문, review 종료 판정과 하단 최신 Progress를 대조해 REV-P2-019 Low 1건을 확정했다.
  • 왜: Phase 3 시작 판정은 과거 검증 이력이 아니라 plan의 현재 상태와 최신 누적 Progress를 소비하므로 두 위치가 완료 사실을 함께 표시해야 하기 때문이다.
  • 어떻게:
    • 대체 RED 완료 상태 검색 — 기대한 Phase 2 완료·Phase 3 진행 가능 exact 상태가 없어 exit 1.
    • 대체 RED 최신 Progress 검색 — 문서 끝에 P2-R15 수정 완료와 열린 항목 없음 기록이 없어 exit 1.
    • P2-R15 Task 체크·inline 검증과 review §5·§6·§7·§8·§9는 수정 완료임을 확인했다.
    • git diff --check HEAD — review 반영 전 exit 0.
  • 변경 범위: 이 review 문서와 plan-task.mdREV-P2-019, P2-R16 판정·후속 계획만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
  • 남은 항목: P2-R16에서 Phase 2 현재 상태와 최신 Progress를 정정하고 문서·Phase 2 Gate 검증을 누적해야 한다.

P2-R16 수정 검증 — 2026-07-27

  • 무엇을: REV-P2-019P2-R16 범위에서 수정했다. Phase 2 상단 현재 상태와 하단 최신 Progress를 P2-R16까지 완료 및 Phase 3 진행 가능 상태로 정렬했다.
  • 왜: 완료 상태를 소비하는 Phase 3 실행자가 과거 6차 남은 항목을 현재 열린 Task로 오해하지 않도록 현재 상태와 누적 이력을 구분해야 하기 때문이다.
  • 어떻게:
    • 대체 GREEN: Phase 2 완료 상태 exact 검색과 최신 Progress 검색이 각각 1건 이상 일치했고, 6차 당시 P2-R15 남은 항목은 이력으로 보존됐다.
    • Full unit/static/build: npm run test:run — 34 files / 147 tests 통과. npm run typecheck, npm run lint, npm run build:dev, npm run build:prod — 모두 exit 0, build는 각각 160 modules 변환.
    • Production boundary: VITE_API_MODE=mock npm run build:prod — 기대한 guard로 exit 1. production worker 파일과 mock bootstrap 문자열은 0건.
    • E2E: npm run e2e — 4 projects / 36 tests 통과. npm run e2e:mock — 4 projects / 28 tests 통과.
    • Diff: plan/review 대상 git diff --check — exit 0.
  • 남은 항목: 현재 review의 열린 확정 항목 없음. P2-GATE와 모든 Phase 2 회귀 수정이 완료돼 Phase 3 진행 가능. mock 통과는 Phase 3의 실제 server integration 완료로 간주하지 않는다.