vignette/docs/ops/e2e-full-sweep-2026-07-27.md
Yun Chan a0311c5957
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s
회기 무발화 0턴 분리, 자기예측 락 불변식 및 TDD 회귀 검증 완료
2026-09-08 23:28:06 +09:00

697 lines
136 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# E2E 전수 순회 체크리스트 — 2026-07-27
> **역사 baseline**: 이 문서는 2026-07-27 당시 전수 순회 인벤토리와 해당 spec의 출발점이다. 현재 남은 직렬 E2E 재현·분류는 [`TODO.md`](../TODO.md)의 `E2E-001`을 따른다.
> **목적**: IA·버튼 단위로 온보딩→가입→학습자→교수자→관리자→설정 전 페이지의 모든 기능을 순회하며
> (1) 의도와 다른 동작, (2) 오류, (3) 나쁜 UI/UX를 잡아낸다.
> **방식**: RED → GREEN. 각 항목은 검증 수단(기존 spec / 신규 spec / 탐색 순회)을 명시하고,
> 실제 실행해 GREEN이 된 것만 `- [x]`로 바꾼다. 가짜 DONE 금지(실행 명령+관측 결과 필수).
> **SSOT**: 상태 결론은 `docs/dev_dashboard.html`에 동기화한다.
>
> 실행 스택: dev-up.ps1 (DB 55432 + API 8000 + web 5173 + engine 9099, `db:true engine:true` 확인됨).
## 전체 항목 수 요약
11개 에이전트가 수합한 기능 인벤토리 기준. 총 **412개 feature**, 그중 **139건**이 의심 결함(risk)으로 보고됐다.
| # | 영역 | 라우트 | 항목 수 | 의심 결함 |
|---|---|---|---|---|
| 1 | 인증·온보딩 (auth) | `/login`, `/onboarding`, `/pending` | 34 | 13 |
| 2 | 학습자 홈 (learner-home) | `/learn`, `/learn/practice`, `/learn/history` | 37 | 12 |
| 3 | 상담 세션 (session) | `/learn/session/:sessionId` | 53 | 11 |
| 4 | 회기 리뷰 (session-review) | `/learn/session/:id/review`, `/teach/session/:id/review` | 37 | 18 |
| 5 | 교수자 콘솔 (professor) | `/teach`, `/teach/analysis` | 36 | 12 |
| 6 | 페르소나 스튜디오 (persona-studio) | `/teach/personas` | 71 | 21 |
| 7 | 관리자 콘솔 (admin) | `/admin`, `/admin/users`, `/admin/access`, `/admin/tickets` | 57 | 15 |
| 8 | 관리자 AI 설정 (admin-ai) | `/admin/ai` | 27 | 9 |
| 9 | 설정 (settings) | `/settings` | 35 | 18 |
| 10 | 공통 셸·GNB·라우팅 가드 (shell) | 전역 GNB, `/`, `*`, dev 라우트 | 25 | 10 |
| | **합계** | | **412** | **139** |
검증 수단 표기 규칙:
- `기존 e2e/{파일}` — 해당 spec 시나리오가 그 기능을 명확히 검증함. 베이스라인 실행으로 GREEN 확인.
- `기존(부분) e2e/{파일} + 탐색 순회` — spec이 일부(레이아웃·인접 흐름)만 덮음. 나머지는 브라우저 순회로 확인.
- `신규 spec 필요` — 이 기능을 검증하는 spec이 없음. RED 스펙부터 작성.
- `탐색 순회` — 자동화 가치가 낮은 순수 표시·이동 항목. 브라우저 순회로만 확인.
## 0. 베이스라인 — 기존 E2E 215개 전체 실행
- [x] `npm run e2e` parallel 단계 (166 tests) 전체 GREEN — 2026-07-27 실행, **166 passed (2.6m)**
- [x] `npm run e2e` single-run 단계 (49 tests) 전체 GREEN — 2026-07-27 실행, **49 passed (15.1m)**
- [x] 실패 항목 → 아래 해당 영역에 RED로 등재 후 수정 — 베이스라인 실패 0건(등재할 항목 없음)
> 베이스라인 근거: dev-up 스택(`db:true engine:true`)에서 `npm run e2e` 1회 완주, 215/215 passed.
> 아래 각 영역에서 `검증: 기존 e2e/...`(부분 아님) 항목은 이 실행을 GREEN 증거로 삼아 체크한다.
>
> **최종 게이트(2026-07-27, 결함 수정 반영 후)**: 신규 `full-sweep-*.spec.ts` 10파일 포함
> `npm run e2e` 재완주 — 병렬 단계 424 수집(fixme 4·모바일 의도 숨김 스킵 2 제외 전 GREEN),
> 직렬 단계 **49/49 passed (14.4m)**, exit 0. 부가 게이트: 백엔드 pytest **412/412**, gateway 29/29,
> typecheck·build·check:api-types·design-ssot·dead-code·duplication(0.03%) 통과,
> layout-visual-gate **15/15**, session-layout **8/8**.
>
> **소유자 결정 구현 후 재완주(2026-07-27, 대시보드 144차)**: 아바타 변경·동의 철회/재동의·기초 우선
> 추천·분석 테이블 가상화·저작 레일 정리·감정 밸런스 차트 반영 후 `npm run e2e` 3차 완주 —
> 병렬 **432 수집**(구 fixme 2건은 실 계약 테스트로 전환 GREEN, 모바일 의도 숨김 스킵 2 제외 전 GREEN),
> 직렬 **49/49 passed (15.9m)**, exit 0. 백엔드 pytest **421/421**(`test_review_valence.py` 9 신규),
> 감정 차트는 실DB 공감 5턴 세션에서 baseline·client 각 5포인트/축 4라벨 라이브 실측.
## 1. 인증·온보딩 (auth)
- [x] `auth-login-config-load` 로그인 설정 조회 (OAuth 구성/허용 도메인) — `GET /auth/config`로 OAuth 구성·dev 로그인 가능 여부·허용 도메인을 받아 버튼 활성/라벨/배지를 결정한다 · 검증: 기존(부분) e2e/auth.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `auth-login-google-button` 단일 Google 계정으로 로그인 버튼 — 클릭 시 `/auth/login?provider=google&next=`로 이동하고, 보조문구는 `모든 Google 계정`이다. Google authorize는 `prompt=select_account`를 유지하고 `hd`를 보내지 않으며 oauthReady가 아니면 비활성화된다 · 검증: 2026-08-29 desktop/mobile 직접 영향 32/32 + auth API 41/41
- [x] `auth-login-generic-google-button` 중복 secondary 제거 — primary와 같은 `startOAuth`를 호출하던 `다른 Google 계정 선택` 버튼을 제거하고 계정 전환은 Google 계정 선택기가 담당한다 · 검증: 2026-08-29 단일 CTA count·옛 버튼 0건 desktop/mobile GREEN
- [x] `auth-login-oauth-status-notice` OAuth 비활성 사유 status 안내 — oauthReady가 아닐 때 role=status로 설정 확인 중/미연결/로컬 테스트 계정 사유를 아이콘과 함께 표시한다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-login-oauth-error-notice` OAuth 콜백 오류 메시지 (?oauth= 쿼리) — 쿼리 사유별(not_configured·domain_not_allowed·access_denied 등) 한국어 안내문과 원본 코드를 표시한다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-login-dev-role-radiogroup` 로컬 테스트 역할 선택 라디오그룹 (학습자/교수자/관리자) — DEV이고 dev_login_enabled일 때만 노출되며 세 역할 버튼으로 dev 로그인 역할을 고른다 · 검증: 기존(부분) e2e/auth.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `auth-login-dev-enter-button` 로컬 테스트 계정으로 계속 버튼 — 선택 역할의 고정 dev 계정으로 `POST /auth/dev-login` 후 initialPathForUser 규칙대로 이동한다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-login-brand-domain-badges` 브랜드 패널 허용 도메인 배지 — allowed_email_domains를 배지로 나열하고 조회 중/미설정 상태를 구분 표시한다 · 검증: 신규 spec 필요
- [x] `auth-login-dev-oauth-unavailable-detection` 로컬 개발에서 OAuth 차단 감지 — redirect_uri가 localhost가 아니면 OAuth 버튼을 비활성화하고 테스트 계정 안내로 대체한다 · 검증: 기존(부분) e2e/auth.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `auth-onboarding-initial-load` 온보딩 초기 데이터 로드 (프로필+약관) — `/users/me``/users/legal-docs`를 병렬 호출해 폼을 프리필하고 약관 본문을 채운다 · 검증: 기존(부분) e2e/auth.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `auth-onboarding-avatar-upload` 상징 아바타 이미지 업로드 — 파일 선택 즉시 multipart로 업로드해 avatar_url을 폼에 반영하고 실패 시 3MB 안내를 띄운다 · 검증: 신규 spec 필요
- [x] `auth-onboarding-avatar-preview` 아바타 미리보기 (이미지/이니셜 폴백) — avatar_url이 있으면 이미지를, 없으면 닉네임 첫 글자를 원 안에 표시한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `auth-onboarding-nickname-input` 닉네임 입력 — 필수 닉네임 텍스트 입력으로 폼 상태를 갱신한다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-self-introduction-textarea` 자기소개 textarea (600자 제한) — maxLength 600의 필수 textarea로 상담 훈련 관련 자기소개를 받는다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-legal-name-input` 이름(법정 이름) 입력 — 필수 입력이며 프로필의 legal_name 또는 display_name으로 프리필된다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-affiliation-input` 소속 입력 (기본값 한신대학교) — 소속 필수 입력으로 프로필 값이 없으면 기본값이 채워진다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-department-input` 학과/부서 입력 — department 필수 텍스트 입력으로 폼 상태를 갱신한다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-grade-level-input` 학년/직위 입력 — grade_level 필수 텍스트 입력으로 폼 상태를 갱신한다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-phone-input` 연락처 입력 — autoComplete=tel 필수 입력으로 연락처를 받는다 · 검증: 기존(부분) e2e/auth.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `auth-onboarding-contact-address-input` 주소/수령지 입력 — autoComplete=street-address 필수 입력으로 주소를 받는다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-terms-checkbox` 서비스 이용약관 동의 체크박스 — terms_accepted를 토글하며 미체크 시 제출이 비활성화된다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-privacy-checkbox` 개인정보 수집·이용 동의 체크박스 — privacy_accepted를 토글하며 미체크 시 제출이 막힌다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-legal-doc-details` 약관/개인정보 문서 펼침 (details/summary) — 두 문서를 details로 펼쳐 제목·버전·본문을 확인하고 미로드 시 폴백을 표시한다 · 검증: 신규 spec 필요
- [x] `auth-onboarding-submit` 가입 설정 완료 제출 버튼 (form submit) — 필수 필드와 두 동의가 모두 충족될 때만 활성화되어 온보딩을 저장하고 역할 홈으로 이동한다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-onboarding-error-notice` 온보딩 로드/저장 오류 알림 — 로드·저장 실패 시 role=alert 문단을 폼 하단에 표시한다 · 검증: 신규 spec 필요
- [x] `auth-pending-status-panel` 승인 대기/접속 보류 상태 표시 — suspended면 '접속 보류', 아니면 '승인 대기' 키커와 이메일·안내를 role=status로 보여준다 · 검증: 신규 spec 필요
- [x] `auth-pending-refresh-button` 승인 상태 새로고침 버튼 — `GET /auth/me`로 세션을 재조회해 승인되면 온보딩 또는 역할 홈으로 이동시킨다 · 검증: 신규 spec 필요
- [x] `auth-pending-signout-button` 다른 계정으로 로그인 버튼 (로그아웃) — 세션을 종료하고 /login으로 이동해 다른 계정 로그인을 허용한다 · 검증: 신규 spec 필요
- [x] `auth-provider-bootstrap` AuthProvider 세션 부트스트랩 — 앱 시작 시 `/auth/me`로 쿠키 세션을 복원하고 auth-expired 이벤트 수신 시 user를 비운다 · 검증: 기존(부분) e2e/learner.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `auth-guard-require-auth` RequireAuth 라우트 가드 — 미인증이면 state.from을 담아 /login으로, 역할 불일치면 자기 역할 홈으로 리다이렉트한다 · 검증: 기존 e2e/learner.spec.ts
- [x] `auth-guard-onboarding-gate` OnboardingGate 리다이렉트 — 온보딩 미완료자를 /onboarding으로 강제 이동시키고 완료자가 /login·/onboarding에 오면 되돌린다 · 검증: 기존 e2e/auth.spec.ts
- [x] `auth-guard-pending-approval-gate` PendingApprovalGate 리다이렉트 — 미승인 로그인 사용자를 어느 경로에서든 /pending으로 회수한다 · 검증: 신규 spec 필요
- [x] `auth-root-redirect` 루트(/) 및 미정의 경로 리다이렉트 — / 진입 시 승인·온보딩 상태에 따라 분기하고 미정의 경로(*)는 /로 회수한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `auth-boot-screen` 인증 부트스트랩 로딩 화면 (BootScreen) — 인증 확인·lazy 라우트 로딩 동안 중앙 정렬 '불러오는 중…' 중립 화면을 띄운다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
**의심 결함(에이전트 리포트)**:
- `auth-login-config-load`: config 로드 실패 시 별도 오류 배너 없이 버튼이 영구 비활성 상태로만 남고, statusMessage는 '연결되지 않았습니다' 문구라서 네트워크 오류와 미설정을 구분해 주지 않는다.
- `auth-login-school-google-button`·`auth-login-generic-google-button`: 2026-08-29 두 버튼이 같은 provider=google 흐름을 호출하던 중복을 단일 CTA로 해소했다. 계정 선택은 `prompt=select_account`로 유지한다. 공개 Pages 번들은 아직 승격 전이라 로컬 증거와 공개 상태를 구분한다.
- `auth-login-oauth-error-notice`: 오류 문단에 role=alert/aria-live가 없어 스크린리더에 즉시 공지되지 않는다.
- `auth-login-dev-role-radiogroup`: role=radio 버튼 그룹이지만 방향키 이동 등 라디오그룹 키보드 시맨틱은 구현되어 있지 않다(Tab으로만 이동).
- `auth-login-dev-enter-button`: 실패 시 err.message를 그대로 노출하는데 ApiError 메시지가 'API 500: ...' 같은 원문이라 사용자 친화적이지 않을 수 있다.
- `auth-onboarding-initial-load`: 로드 실패 시 재시도 버튼이 없어 새로고침 외 복구 수단이 없고, 약관 로드 실패여도 동의 체크는 가능해 문서 미확인 동의가 성립한다.
- `auth-onboarding-avatar-upload`: 클라이언트 측 파일 크기/타입 사전 검증이 없어 3MB 제한을 서버 거절 후에야 알게 되고, 서버 오류 상세는 콘솔에만 남는다.
- `auth-onboarding-phone-input`: type=text 자유 입력이라 전화번호 형식 검증이 전혀 없다.
- `auth-onboarding-terms-checkbox`: 약관 본문을 펼쳐 보지 않아도 체크 가능해 열람 강제가 없다.
- `auth-onboarding-submit`: 실패 시 '온보딩 저장에 실패했습니다' 일반 문구만 표시하고 서버의 필드별 검증 상세(ApiError.detail)는 콘솔에만 버려진다. 또한 성공 후 initialPathForUser가 아닌 roleHomePath로 이동해 admin_access만 있는 비-admin 역할 사용자의 도착지가 로그인 직후 규칙(/admin)과 어긋난다.
- `auth-pending-refresh-button`: refresh가 네트워크 오류로 실패하면 AuthProvider가 user를 null로 만들어 RequireAuth가 아무 안내 없이 /login으로 튕긴다(여전히 pending인 경우에도 '아직 승인 전'이라는 피드백이 전혀 없음). 버튼 아이콘도 refresh가 아닌 'settings'라 의미가 어긋난다.
- `auth-guard-pending-approval-gate`: pending 사용자는 /login에도 접근할 수 없어(항상 /pending으로 회수) 계정 전환 경로가 PendingApproval의 로그아웃 버튼 하나뿐이다.
## 2. 학습자 홈 (learner-home)
- [x] `learner-home-guard-require-auth` RequireAuth(roles=[learner]) 라우트 가드 — 미인증은 /login, learner 권한 없는 역할은 자기 역할 홈으로 보낸다(admin·superAdmin은 통과) · 검증: 기존 e2e/learner.spec.ts
- [x] `learner-home-guard-pending-approval` PendingApprovalGate 리다이렉트 — 미승인 사용자가 /learn 계열에 오면 state.from을 담아 /pending으로 보낸다 · 검증: 신규 spec 필요
- [x] `learner-home-guard-onboarding` OnboardingGate 리다이렉트 — onboardingCompletedAt이 null이면 /onboarding으로 보낸다 · 검증: 기존 e2e/auth.spec.ts
- [x] `learner-home-nav-sidebar` 좌측 사이드바 학습자 내비게이션 — 대시보드·학습·기록·설정 NavLink를 제공하고 활성 항목을 알약으로 표시한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `learner-home-initial-load` 마운트 시 페르소나·회기·대시보드 병렬 로드 — allSettled로 3개 API를 병렬 요청하고 각 섹션이 독립된 로딩/오류 상태를 유지한다 · 검증: 기존 e2e/learner.spec.ts + e2e/readiness.spec.ts
- [x] `learner-home-head-status-live` 헤더 상태 배지(aria-live) — 사용 가능 페르소나 수와 기록 상태를 aria-live=polite로 헤더 우측에 표시한다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-metric-cards` 학습 현황 지표 카드 4개 — 진행 회기·리뷰 대기·최근 평가·라포 흐름 카드를 버튼으로 렌더하고 각각 기록/연습 화면으로 이동시킨다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-midwidth-tabs` 중간 폭(≤1180px) 대시보드 가로 탭 3개 — 오늘의 회기·기록 리뷰·성장 지표 탭으로 그리드를 전환하고 데스크톱에서는 2컬럼을 유지한다 · 검증: 기존(부분) e2e/layout-visual-gate.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `learner-home-dash-new-session-shortcut` 오늘의 회기 작업 헤더 '새 회기 선택' 버튼 — 클릭 시 /learn/practice로 이동해 연습 대상 선택 화면을 연다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `learner-home-dash-recap-card` 스포트라이트 회기 리캡 카드 — 우선순위로 고른 회기의 상세를 불러와 아바타·단계·진행률·마지막 발화를 보여준다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-coach-cta` AI 코치 카드와 기본 행동 CTA 버튼 — 규칙 기반 코치 문구와 힌트 칩을 만들고 CTA로 세션·리뷰·연습 중 적절한 곳으로 보낸다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-recommend-card` 다음 연습 추천 카드 버튼 — 리뷰 대기 유무와 최근 평균 턴 수에 따라 문구를 바꿔 기록 또는 연습 화면으로 이동시킨다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-recent-feedback` 최근 피드백 패널(최신 1건 리뷰 이동) — 대시보드 recent_feedback 첫 항목을 버튼으로 보여주고 해당 회기 리뷰로 이동시킨다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-recent-records` 최근 기록 패널(최근 4건 + 전체 보기) — 최근 회기 4건을 컴팩트 행으로 보여주고 상태에 따라 세션 또는 리뷰로 이동시킨다 · 검증: 기존(부분) e2e/learner.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `learner-home-dash-review-queue` 리뷰 대기 패널(review_ready 상위 3건) — review_ready 상위 3건에서 'AI 피드백' 버튼으로 리뷰 화면에 진입시킨다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-weakness-insights` 보완할 부분 패널(파생 인사이트 최대 3건) — growth 데이터에서 하락 추세·낮은 라포 등을 결정론적으로 도출해 버튼으로 제시한다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-rapport-progress` 내담자와 쌓은 라포 누적 바(상위 3명) — 회기가 있는 페르소나 상위 3명의 rapport_percent를 progressbar로 표시한다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-milestones` 연습 이정표 배지(achievements) — achievements를 상태별 배지로 나열하고 상세는 title 툴팁으로만 제공한다 · 검증: 신규 spec 필요
- [x] `learner-home-dash-recap-fetch-states` 리캡 상세 로딩·오류 상태 — 스포트라이트 회기가 바뀔 때마다 상세를 다시 불러오고 stale 응답은 session_id 비교로 무시한다 · 검증: 신규 spec 필요
- [x] `learner-home-practice-persona-listbox` 연습 페르소나 선택 리스트(role=listbox/option) — 페르소나별 옵션 버튼으로 선택 상태를 바꾸고 degraded·비DB 항목은 비활성 처리한다 · 검증: 기존 e2e/learner.spec.ts
- [x] `learner-home-practice-persona-skeleton` 페르소나 목록 스켈레톤·오류 상태 — 로딩 중 스켈레톤 3개를, 실패 시 role=alert 안내를 표시한다 · 검증: 신규 spec 필요
- [x] `learner-home-practice-preview-pane` 선택 페르소나 프리뷰 — 선택 페르소나의 인적사항·주호소·난도/접근/음성을 보여주고 사용 가능 항목이 없으면 카탈로그 안내로 전환한다 · 검증: 기존(부분) e2e/learner.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `learner-home-practice-start-session` '새 회기 시작' 버튼과 회기 시작 흐름 — 사용 가능 여부를 재검증한 뒤 세션 프리스타트로 이동시킨다 · 검증: 기존 e2e/learner.spec.ts
- [x] `learner-home-practice-activity-stats` 연습 기록 통계 3칸 — 진행 중·리뷰 가능·누적 회기 수를 표시하고 로딩·오류 시 대시로 대체한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `learner-home-practice-existing-sessions` 선택 페르소나의 기존 회기 목록(최근 5건) — 같은 페르소나의 최근 5건을 상태·단계·턴·진행률과 함께 카드로 나열한다 · 검증: 기존(부분) e2e/learner.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `learner-home-session-card-continue` 회기 카드 '이어하기' 버튼(진행 중 회기) — active 회기에서 세션 화면으로 이동해 대화를 재개한다 · 검증: 기존 e2e/learner.spec.ts
- [x] `learner-home-session-card-review` 회기 카드 '리뷰'/'기록' 버튼(종료 회기) — review_ready·보관 여부에 따라 라벨을 바꿔 리뷰 화면으로 이동한다 · 검증: 기존 e2e/learner.spec.ts
- [x] `learner-home-session-card-repeat` 회기 카드 '다시 연습' 버튼 — 같은 페르소나 코드로 새 회기 준비 화면을 연다 · 검증: 기존(부분) e2e/learner.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `learner-home-session-card-archive-toggle` 회기 카드 '보관'/'복원' 토글 버튼 — 종료 회기에서만 보관/복원을 호출하고 성공 시 회기·대시보드를 재조회한다 · 검증: 기존 e2e/learner.spec.ts
- [x] `learner-home-history-overview-cards` 기록 상태 요약 필터 카드 4개 — 전체·진행 중·리뷰 필요·보관됨 버킷 카드(aria-pressed)로 historyFilter를 전환한다 · 검증: 신규 spec 필요
- [x] `learner-home-history-search` 기록 검색 입력 — 코드·이름·단계·상태·날짜 라벨을 이어붙인 문자열에 부분 일치로 즉시 필터링한다 · 검증: 신규 spec 필요
- [x] `learner-home-history-filter-chips` 기록 상태 필터 칩(도구 행) — 요약 카드와 상태를 공유하는 칩 버튼으로 동일 필터를 전환한다 · 검증: 신규 spec 필요
- [x] `learner-home-history-list` 필터·검색 적용된 회기 카드 목록 — 정렬된 회기를 필터·검색으로 걸러 액션 버튼·진행률 바와 함께 카드로 나열한다 · 검증: 기존(부분) e2e/learner.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `learner-home-history-archive-notice` 보관/복원 결과 안내 배너(role=status) — 보관·복원 성공/실패 문구를 목록 위에 표시한다 · 검증: 신규 spec 필요
- [x] `learner-home-history-archive-guide` 아카이브 기준 안내 패널(읽기 전용) — 진행 중·리뷰 필요·보관됨 각각의 의미를 정적 텍스트로 안내한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `learner-home-history-persona-progress` 페르소나별 진행 행 버튼 — 페르소나별 회기·진행·리뷰·평균 턴 요약을 행 버튼으로 보여주고 클릭 시 해당 페르소나로 연습 화면에 진입한다 · 검증: 신규 spec 필요
- [x] `learner-home-session-list-states` 회기 목록 공용 로딩·오류·빈 상태 — 로딩 문구·인라인 경고·컨텍스트별 빈 메시지를 구분 표시한다 · 검증: 신규 spec 필요
**의심 결함(에이전트 리포트)**:
- `learner-home-initial-load`: 실패 시 재시도 버튼이 어디에도 없어 사용자는 새로고침 외에 복구 수단이 없음
- `learner-home-head-status-live`: loadState가 error일 때도 삼항 조건이 '확인 중'을 영구 표시해 페르소나 로드 실패가 헤더에서는 드러나지 않음
- `learner-home-dash-midwidth-tabs`: role=tab인데 화살표 키 이동·aria-controls/tabpanel 연결이 없어 WAI-ARIA 탭 패턴 미준수
- `learner-home-dash-recap-card`: 아바타 rapport 값이 실제 데이터가 아닌 하드코딩(정리 단계 0.72, 그 외 0.38)이고, 상세 로드 실패(error)와 발화 없음(empty)이 같은 문구로 표시되어 구분 불가
- `learner-home-dash-coach-cta`: sessionLoadState=error일 때 CTA 라벨이 '기록 다시 확인'이지만 실제 동작은 재요청이 아니라 /learn/practice 이동이라 라벨과 동작이 불일치
- `learner-home-dash-recent-feedback`: 배지에는 recentFeedback.length건으로 표시하면서 목록에는 1건만 노출해 나머지 피드백에 접근할 UI가 없음
- `learner-home-dash-milestones`: 상세 설명이 title 툴팁뿐이라 터치 기기·스크린리더에서 접근 불가
- `learner-home-practice-persona-listbox`: role=listbox/option 구조인데 화살표 키 탐색·aria-activedescendant가 없어 커스텀 리스트박스 키보드 패턴 미구현(버튼 Tab 이동만 가능)
- `learner-home-practice-persona-skeleton`: '다시 시도해 주세요' 안내만 있고 실제 재시도 버튼은 제공하지 않음
- `learner-home-session-card-repeat`: 해당 페르소나가 현재 사용 불가(degraded/비DB)로 바뀐 경우에도 비활성화 없이 이동을 허용해 Session 페이지에서야 실패를 만나게 됨
- `learner-home-session-card-archive-toggle`: archiveNotice 표시 블록이 history 뷰에만 렌더링되어, practice 뷰의 기존 회기 목록에서 보관/복원(특히 실패)을 눌러도 결과 안내가 화면에 전혀 나타나지 않음
- `learner-home-history-archive-notice`: 자동 해제 타이머가 없어 다음 토글 전까지 이전 안내가 계속 남고, 실패 원인(HTTP 상태 등)은 console.warn으로만 남아 사용자에게 전달되지 않음
## 3. 상담 세션 (session)
- [x] `session-guard-require-auth` RequireAuth(learner) 라우트 가드 — 미인증은 /login(state.from 보존), learner 권한 없으면 자기 역할 홈으로 보낸다 · 검증: 기존 e2e/learner.spec.ts
- [x] `session-guard-pending-approval` PendingApprovalGate 가드 — 미승인 계정은 /pending으로 리다이렉트한다 · 검증: 신규 spec 필요
- [x] `session-guard-onboarding` OnboardingGate 가드 — 온보딩 미완료 사용자는 /onboarding으로 리다이렉트한다 · 검증: 기존(부분) e2e/auth.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-route-empty-param-redirect` 빈 라우트 파라미터 리다이렉트 — 파라미터가 비면 /learn으로 replace 이동하고, 페르소나 코드면 세션 상태를 초기화한다 · 검증: 신규 spec 필요
- [x] `session-persona-load` 페르소나 정보 로드(로딩/누락/오류 상태) — 코드를 대조해 아바타·컨텍스트를 구성하고 상태별 안내를 프리스타트에 표시한다 · 검증: 기존 e2e/learner.spec.ts
- [x] `session-resume-load` 기존 회기 재개(resume) 로드 — 세션 ID면 서버 스냅샷으로 턴·단계·경과·진행률을 복원하고 방금 시작한 회기는 재로딩을 건너뛴다 · 검증: 기존 e2e/session-layout.spec.ts
- [x] `session-theory-mode-select` 이론모드 세그먼트 선택(인간중심/CBT/통합) — 시작 전 이론모드를 고르며 페르소나 theory_target 첫 항목이 기본 선택된다 · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `session-goal-toggle` 이번 회기 목표 단계 토글(라포/탐색/개입/정리) — 목표 단계를 1~4개 토글하며 0개면 시작 버튼이 비활성화된다 · 검증: 기존(부분) e2e/session-mvp.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-consent-checkbox` 연습 기록 처리 동의 체크박스 — 동의 이력이 없는 learner에게만 노출되며 체크해야 저장 버튼이 활성화된다 · 검증: 신규 spec 필요
- [x] `session-consent-save` 동의 저장 버튼 — 동의를 서버에 기록하고 성공 시 pos 신호, 실패 시 오류 문구를 표시한다 · 검증: 신규 spec 필요
- [x] `session-start-button` 회기 시작 버튼 — 준비·동의·목표 조건이 충족될 때만 활성화되어 세션을 생성하고 URL을 실제 세션 ID로 교체한 뒤 음성 가용성을 사전 확인한다 · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `session-prestart-status-messages` 프리스타트 상태/오류 문구 표시 — 페르소나 로딩·없음·실패·사용 불가와 시작 오류를 구분된 문구로 표시한다 · 검증: 기존(부분) e2e/learner.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-nav-back-history` 세션바 '기록으로' 버튼 — 회기 시작 후 상단 세션바에서 /learn/history로 이동한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-nav-learn-home` 세션바 '학습 홈' 버튼 — /learn 학습자 홈으로 이동한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-nav-review-sessionbar` 세션바 리뷰 이동 버튼 — 종료 후에만 노출되어 리뷰로 이동하며 reviewReady에 따라 라벨이 바뀐다 · 검증: 기존(부분) e2e/session-mvp.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-time-alarms` 시간 알람(10분 전 경고·시간 만료) — 경고 구간 진입 시 warn 신호를 1회 띄우고 만료 시 timeUp으로 전환해 종료 다이얼로그를 자동으로 연다 · 검증: 신규 spec 필요
- [x] `session-timebar-end-button` 시간 만료 바 '종료하고 리뷰 보기' 버튼 — role=alert 시간 바에서 만료 시 노출되어 종료 확인 다이얼로그를 연다 · 검증: 신규 spec 필요
- [x] `session-phase-tracker` 회기 단계 트래커(헤더 가로 + 좌측 세로) — 4단계의 완료·진행·예정 상태와 목표 배지, 서버 progress 기반 누적 게이지를 표시한다 · 검증: 기존(부분) e2e/session-layout.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `session-ctx-collapse` 내담자 컨텍스트 패널 접기/펼치기 — aria-expanded 토글로 내담자 이름·메타·호소/대상/난도 행을 접고 펼친다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-progress-panel` 세션 진행 패널(경과/남은 시간/발화 수) — 경과·남은 시간·저장 발화 수를 표시하고 목표 달성 시 축하 문구를 띄운다 · 검증: 기존(부분) e2e/session-layout.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `session-avatar-display` 내담자 아바타 + 음성 오브 표시 — 상태·개방도·단계에서 파생한 표정을 렌더하고 TTS 재생 중 Analyser로 입모양을 구동한다 · 검증: 기존 e2e/avatar-expression.spec.ts
- [x] `session-transcript-autoscroll` 자막 자동 스크롤 + '최신으로' 점프 버튼 — 새 발화 시 하단으로 따라가되 위로 스크롤하면 해제하고 복귀 버튼을 띄운다 · 검증: 신규 spec 필요
- [x] `session-transcript-utterances` 발화 목록(부분 스트리밍/저장 실패/응답 대기 표시) — 화자·타임코드와 함께 스트리밍 캐럿·저장 실패 노트·응답 대기 인디케이터를 표시한다 · 검증: 기존 e2e/session-mvp.spec.ts · 베이스라인 215/215 GREEN
- [x] `session-transcript-empty-state` 자막 빈 상태 안내 — 발화가 없으면 종료 여부에 따라 첫 질문 대기 또는 종료 기록 문구를 보여준다 · 검증: 신규 spec 필요
- [x] `session-transcript-coach-mark` 학습자 발화의 코칭 마크(C) 버튼 — 코칭 이력이 있는 턴에 C 배지를 붙이고 클릭 시 해당 턴 필터로 이력 모달을 연다 · 검증: 기존 e2e/session-mvp.spec.ts
- [x] `session-compose-input` 학습자 발화 텍스트 입력(Enter 전송) — Enter 전송·Shift+Enter 줄바꿈이며 종료·일시정지·전송 중에는 비활성화된다 · 검증: 기존 e2e/session-mvp.spec.ts
- [x] `session-send-button` '보내기' 버튼(텍스트 턴 SSE 스트림) — 낙관적 발화 추가 후 SSE로 응답 토큰을 흘리고 done에서 단계·개방도·진행률을 반영한다 · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `session-stream-error-handling` 스트림 오류 처리(롤백·문구 매핑) — 실패 시 낙관적 발화를 제거하고 입력을 복원하며 오류 코드를 사용자 문구로 매핑한다 · 검증: 기존 e2e/session-layout.spec.ts
- [x] `session-turn-error-display` 턴 오류 알림 섹션 — turnError를 role=alert 섹션에 '엔진 연결/회기 종료' 제목과 함께 표시한다 · 검증: 기존(부분) e2e/session-layout.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-crisis-gate` 위기 안전게이트 — 위기 신호 수신 시 회기를 일시정지하고 마이크를 끄며 109 등 위기 자원과 안전 패널을 강제로 연다 · 검증: 기존 e2e/session-mvp.spec.ts + e2e/session-persistence.spec.ts
- [x] `session-crisis-tel-link` 위기 자원 전화 링크 — 위기 자원 번호를 tel: 링크로 제공해 즉시 전화를 걸 수 있게 한다 · 검증: 신규 spec 필요
- [x] `session-safety-panel-collapse` 안전 점검 패널 접기/펼치기 — 토글로 접되 위기 신호가 있으면 접힘을 무시하고 항상 펼친다 · 검증: 기존(부분) e2e/session-mvp.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-live-signal` 라이브 신호(앰비언트 도트 + 최근 흐름) — 이벤트마다 도트를 6초 페이드로 띄우고 최근 5개 시퀀스를 누적하며 모드별로 노출을 달리한다 · 검증: 신규 spec 필요
- [x] `session-feedback-mode-segmented` 화면 모드 세그먼트(몰입/상태 신호/코칭) — 피드백 강도 3모드를 전환하고 코칭 버튼에 남은 기회 배지를 붙인다 · 검증: 기존(부분) e2e/session-mvp.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `session-coach-auto-request` 턴 완료 후 라이브 코칭 자동 요청 — 코칭 모드에서 남은 기회를 확인해 코칭을 요청하고 409면 소진 문구를, 비코칭 모드면 넛지로 보관한다 · 검증: 기존 e2e/session-mvp.spec.ts + e2e/session-persistence.spec.ts
- [x] `session-coach-card` AI 코치 카드(기회 도트·크레딧 펄스·degraded) — 남은 기회 도트와 크레딧 펄스, 제안 문구, degraded·runtime 경고를 표시한다 · 검증: 기존 e2e/session-mvp.spec.ts
- [x] `session-coach-nudge` 코칭 넛지 버튼 — 코칭 화면이 닫힌 채 기회가 대기 중이면 넛지를 띄우고 클릭 시 코칭 모드로 전환해 즉시 요청한다 · 검증: 신규 spec 필요
- [x] `session-coach-evidence-modal` 코칭 근거 모달('근거 보기') — 판단·다음 문장·RAG 근거 자료를 다이얼로그로 보여주고 Escape·오버레이·닫기로 닫는다 · 검증: 기존 e2e/session-mvp.spec.ts
- [x] `session-coach-history-modal` 코칭 이력 모달('이력 보기'/턴 마크) — 턴별 코칭 이벤트를 시간순으로 보여주고 로딩/오류/빈 상태와 runtime 경고를 구분한다 · 검증: 기존 e2e/session-mvp.spec.ts
- [x] `session-meters-collapse` 관찰 신호 패널 접기 + 게이지 3종 — 방어·유효 개방도·라포 게이지를 서버 progress 또는 휴리스틱으로 표시하고 접으면 라포 %를 요약한다 · 검증: 신규 spec 필요
- [x] `session-mic-toggle` 마이크 버튼(음성 녹음 시작/발화 전송) — 권한 요청 후 음성 WS를 열어 PCM 청크를 보내고 다시 누르면 audio_end로 발화를 확정한다 · 검증: 기존 e2e/voice-success.spec.ts
- [x] `session-voice-ws-events` 음성 WebSocket 이벤트 처리 — ready/state/transcript/reply/tts_end/degraded/error 이벤트를 상태·자막·신호에 반영하고 바이너리는 TTS 버퍼로 쌓는다 · 검증: 기존 e2e/voice-success.spec.ts
- [x] `session-voice-error-recovery` 음성 오류/연결 종료 시 발화 실패 처리 — reply 확정 전 WS가 끊기면 대기 발화를 '저장 실패'로 표시하고 마이크를 안전 상태로 되돌린다 · 검증: 기존 e2e/session-mvp.spec.ts
- [x] `session-tts-playback` 내담자 TTS 재생(WebAudio+Analyser, Audio 폴백) — 합성 mp3를 WebAudio로 재생하며 입모양을 구동하고 실패 시 Audio 폴백·degraded 안내로 넘어간다 · 검증: 기존 e2e/voice-success.spec.ts
- [x] `session-voice-skip` 음성 '건너뛰기' 버튼 — TTS 재생 중에만 노출되어 재생·버퍼를 끊고 소켓을 닫은 뒤 idle로 복귀한다 · 검증: 신규 spec 필요
- [x] `session-pause-toggle` 일시정지/이어가기 버튼 — 일시정지 시 타이머·음성 캡처·재생을 모두 멈추고 이어가기 시 listening으로 복귀한다 · 검증: 신규 spec 필요
- [x] `session-end-open` '회기 종료' 버튼 — 종료 확인 다이얼로그를 열며 종료 처리 중에는 비활성화된다 · 검증: 기존 e2e/session-mvp.spec.ts
- [x] `session-end-dialog` 회기 종료 확인 다이얼로그 — 취소/종료 버튼 사이 Tab 포커스 트랩과 Escape·오버레이 닫기, 포커스 복원을 제공한다 · 검증: 기존(부분) e2e/session-mvp.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `session-end-confirm` '종료하고 리뷰 보기' 확정 버튼 — 종료 API 성공 시 리뷰로 replace 이동하고 실패 시 리뷰로 넘어가지 않고 오류를 표시한다 · 검증: 기존 e2e/session-mvp.spec.ts
- [x] `session-review-button-controlbar` 컨트롤바 리뷰 이동 버튼(종료 후) — 종료 후 일시정지·종료 자리를 대체해 리뷰로 이동하며 준비 상태에 따라 라벨이 바뀐다 · 검증: 기존(부분) e2e/session-mvp.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-keyboard-shortcuts` 키보드 단축키(Space=마이크, P=일시정지) — 입력창 포커스가 아닐 때 Space로 마이크를, P로 일시정지를 토글한다 · 검증: 신규 spec 필요
- [x] `session-elapsed-live-region` 경과 시간 스크린리더 라이브 리전 — sr-only aria-live 요소로 경과 시간을 보조기기에 알린다 · 검증: 신규 spec 필요
- [x] `session-mobile-context-strip` 모바일 컨텍스트 요약 스트립 — 모바일 폭에서 회기 상태·내담자·마이크·단계·안전 상태를 한 줄로 요약한다 · 검증: 기존(부분) e2e/session-layout.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
**의심 결함(에이전트 리포트)**:
- `session-start-button`: consent_required 외 모든 시작 실패가 단일 일반 문구로 뭉개져 원인(권한/404 등) 구분이 안 됨
- `session-nav-back-history`: 진행 중 회기에서 확인 없이 즉시 이탈 가능(회기는 서버에 남지만 이탈 경고 없음)
- `session-nav-learn-home`: 진행 중 회기 이탈 확인 다이얼로그 없음
- `session-timebar-end-button`: timeUp 이후에도 텍스트 입력이 클라이언트에서 막히지 않고 서버 session_time_over 오류에 의존함(의도된 정리 유예일 수 있으나 안내 부족)
- `session-stream-error-handling`: 오류 롤백 시 서버에 이미 저장됐을 수 있는 턴과 클라이언트 상태가 어긋나 재전송하면 중복 턴 가능
- `session-feedback-mode-segmented`: 세그먼트 3개 버튼에 type="button" 누락, 선택 상태가 aria-pressed 없이 클래스로만 표현됨
- `session-coach-evidence-modal`: 초기 포커스와 Escape만 있고 Tab 포커스 트랩이 없어 모달 밖으로 포커스 이탈 가능(종료 다이얼로그는 트랩 있음)
- `session-coach-history-modal`: 근거 모달과 동일하게 Tab 포커스 트랩 없음
- `session-mic-toggle`: WS onmessage가 소켓 생성 시점의 elapsed를 클로저로 고정해 음성 발화 타임코드가 마이크 연결 시각으로 표시됨
- `session-keyboard-shortcuts`: 한글 IME 등 contentEditable은 typing 예외에서 빠져 있고, P 단축키는 다이얼로그가 열린 상태에서도 동작해 모달 뒤 상태를 바꿀 수 있음
- `session-elapsed-live-region`: 매초 갱신되는 aria-live=polite라 스크린리더가 1초마다 경과 시간을 읽어 소음이 될 수 있음
## 4. 회기 리뷰 (session-review)
- [x] `session-review-guard-require-auth` RequireAuth 라우트 가드 — 미인증은 /login, 역할 불일치(학습자/교수자 경로)면 자기 역할 홈으로 replace 리다이렉트한다 · 검증: 신규 spec 필요
- [x] `session-review-guard-pending-onboarding` PendingApprovalGate·OnboardingGate 전역 가드 — 미승인은 /pending, 온보딩 미완료는 /onboarding으로 보내고 로딩 중 BootScreen을 띄운다 · 검증: 신규 spec 필요
- [x] `session-review-initial-load` 리뷰 데이터 초기 로드와 로딩 상태 — 축어록·평가·워크시트·교수자 검토를 한 번에 불러오고 로딩 중 빈 카드를 표시한다 · 검증: 기존 e2e/session-review.spec.ts
- [x] `session-review-error-state` 리뷰 로드 오류 상태 — sessionId 누락·로드 실패 시 '리뷰를 표시할 수 없습니다' 카드에 오류 메시지를 보여준다 · 검증: 신규 spec 필요
- [x] `session-review-teacher-auto-redirect` 교수자 자동 리다이렉트 — 학습자 경로에서 teacher 권한자가 teacherReview 응답이나 403을 만나면 교수자 리뷰 경로로 옮긴다 · 검증: 기존 e2e/session-review.spec.ts
- [x] `session-review-ready-polling` 평가 준비 상태 폴링 — 평가 대기 조건이면 1.5초 간격 최대 60회까지 리뷰를 재조회한다 · 검증: 기존 e2e/session-review.spec.ts
- [x] `session-review-tabs` 리뷰 영역 가로 탭(축어록/피드백/워크시트) — role=tablist 탭 3개로 영역을 전환하며 aria-selected로 활성 탭을 표시한다 · 검증: 기존(부분) e2e/session-review.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `session-review-btn-teach-console` 교수 콘솔로 이동 버튼 — 교수자 화면에서만 노출되며 클릭 시 /teach로 이동한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-review-btn-retry-evaluation` AI 평가 재시도 버튼 — 평가 실패 상태의 종료 회기에서만 노출되어 session_end 범위 재평가를 요청하고 리뷰를 다시 로드한다 · 검증: 기존 e2e/session-review.spec.ts + e2e/session-persistence.spec.ts
- [x] `session-review-btn-audio` 오디오 다시 듣기 버튼 — audioUrl이 있을 때만 활성화되어 새 탭으로 오디오 URL을 연다 · 검증: 신규 spec 필요
- [x] `session-review-btn-pdf` PDF 내보내기 버튼 — pdfExportUrl이 있을 때만 활성화되어 새 탭으로 내보내기 URL을 연다 · 검증: 신규 spec 필요
- [x] `session-review-btn-share` 공유 URL 생성·복사 버튼 — 종료된 회기의 학습자 화면에서 공유 링크를 만들어 클립보드에 복사하고 라벨을 바꾼다 · 검증: 신규 spec 필요
- [x] `session-review-share-note` 공유 URL·오류 노트 표시 — 생성된 URL 또는 실패 메시지를 요약 카드 하단 노트로 표시한다 · 검증: 신규 spec 필요
- [x] `session-review-transcript-filter` 축어록 전체/내 발화만 필터 토글 — aria-pressed 칩 2개로 전체·학습자 발화 보기를 전환하며 발화가 없으면 비활성화된다 · 검증: 신규 spec 필요
- [x] `session-review-turn-jump` 코멘트 타임스탬프로 발화 점프 — jumpTo 버튼으로 해당 발화를 강조·중앙 정렬하고 필요하면 필터를 자동 해제한다 · 검증: 신규 spec 필요
- [x] `session-review-transcript-display` 축어록 타임라인 표시(칩·PII 마스킹·빈 상태) — 타임스탬프·기법/비언어 칩·콜아웃을 표시하고 PII 플레이스홀더를 한국어 라벨로 치환한다 · 검증: 기존 e2e/session-review.spec.ts
- [x] `session-review-summary-band` 리뷰 요약 밴드와 준비 상태 표시 — 강조 태그 포함 요약과 근거 발화 수·평가 상태·도달 단계를 보여주고 엔진 오류 흔적은 대체 문구로 마스킹한다 · 검증: 기존(부분) e2e/session-review.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `session-review-valence-chart` 감정 밸런스 타임라인 차트 — 내담자 정서가와 상담자 기준선을 스무딩 SVG로 그리고 데이터가 없으면 빈 상태를 보여준다 · 검증: 기존(부분) e2e/session-review.spec.ts + 탐색 순회 · 빈 상태 GREEN — 차트 데이터 경로는 서버 미구현(결함 로그 #21, 로드맵)
- [x] `session-review-phase-flow` 회기 흐름 단계 바 — 단계별 weight 비율 가로 바를 그리고 구간마다 단계명·비율 툴팁을 붙인다 · 검증: 신규 spec 필요
- [x] `session-review-rubric-display` 기법 사용 분포 카드 — 기법별 사용 빈도 바와 적절/살펴보기 품질 배지를 표시하고 평가가 없으면 대기 상태를 보여준다 · 검증: 신규 spec 필요
- [x] `session-review-good-growth-points` 좋았던 순간·다음 회기 개선점 카드 — 근거 있는 강점과 개선점(최대 3개)을 점프 버튼과 함께 표시한다 · 검증: 신규 spec 필요
- [x] `session-review-client-feedback` 마지막 내담자 반응 표시 — AI 내담자가 생성한 마지막 피드백 인용문을 보여주고 없으면 빈 문구를 표시한다 · 검증: 기존(부분) e2e/session-mvp.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-review-worksheet-edit` 사례개념화 워크시트 항목 편집 — 학습자 화면에서 섹션별 textarea 수정 시 confidence를 자동 조정하고 dirty 상태로 전환한다(교수자는 읽기 전용) · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `session-review-worksheet-save` 워크시트 저장 버튼 — 변경이 있을 때만 활성화되어 초안과 limitations를 PUT 저장하고 라벨을 저장→저장됨으로 바꾼다 · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `session-review-worksheet-evidence-jump` 워크시트 근거 발화 이동 버튼 — 워크시트 항목의 첫 근거 인용 버튼으로 해당 발화에 점프한다 · 검증: 신규 spec 필요
- [x] `session-review-prepost-load` 사전/사후 점수 원장 로드 — 마운트 시 phase3-pilot-draft 파일럿 측정값을 불러와 초안·기준선과 완료쌍 배지를 채운다 · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `session-review-prepost-input` 사전/사후 점수 입력(3척도×2시점) — 자기효능감·기술숙련도·수련만족도 사전/사후 값을 1~5로 편집하고 범위 밖 입력은 오류로 막는다 · 검증: 기존 e2e/session-review.spec.ts
- [x] `session-review-prepost-save` 사전/사후 점수 저장 버튼 — 유효 변경만 있을 때 활성화되어 키별 순차 PUT 후 원장을 재조회한다 · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `session-review-teacher-note-input` 교수자 검토 메모 입력 — 교수자 전용 textarea(2000자)에 다음 지도 메모를 작성한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `session-review-teacher-save-viewed` 메모 저장(검토 중) 버튼 — 검토 상태를 viewed로 두고 메모·워크시트 메모를 PUT 저장한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `session-review-teacher-close` 검토 완료 버튼 — 종료·평가 준비된 회기에서만 활성화되어 검토 상태를 closed로 저장하고 완료 시각을 표시한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `session-review-teacher-worksheet-note` 워크시트 검수 메모 입력 — 수정요청·반려 사유를 적는 textarea로 검수 결정·메모 저장 시 함께 PUT된다 · 검증: 신규 spec 필요
- [x] `session-review-teacher-worksheet-approve` 워크시트 승인 버튼 — 학습자 저장본이 있는 종료 회기에서 worksheet_status=approved를 저장한다 · 검증: 기존(부분) e2e/session-persistence.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `session-review-teacher-worksheet-request-changes` 워크시트 수정요청 버튼 — 같은 조건에서 worksheet_status=changes_requested를 저장하고 검수 배지를 갱신한다 · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `session-review-teacher-worksheet-reject` 워크시트 반려 버튼 — 같은 조건에서 worksheet_status=rejected를 저장하며 저장 중에는 세 결정 버튼이 모두 비활성화된다 · 검증: 신규 spec 필요
- [x] `session-review-eval-scope-notice` AI 평가 범위 안내 박스 — fast-loop/deep-loop 구현 범위와 임상팀 확정 필요 항목을 정적으로 안내한다 · 검증: 기존 e2e/session-review.spec.ts
- [x] `session-review-head-meta` 회기 헤더·상태 스탯 표시 — 내담자·날짜·소요시간·페르소나와 화면 배지, SESSION/SUPERVISOR 상태와 세션 ID를 표시한다 · 검증: 기존(부분) e2e/session-review.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
**의심 결함(에이전트 리포트)**:
- `session-review-error-state`: 오류 화면에 재시도/뒤로가기 액션이 없어 새로고침 외 복구 수단이 없다.
- `session-review-teacher-auto-redirect`: 403 detail 문자열('session does not belong to user') 완전 일치에 의존해 백엔드 문구가 바뀌면 리다이렉트가 조용히 깨진다.
- `session-review-ready-polling`: 60회 한도 도달 시 아무 안내 없이 폴링이 멈춰 사용자는 '대기' 표시가 영구 상태인지 알 수 없다. 또한 폴링마다 setData로 워크시트 초안 상태가 리셋되어 입력 중이던 학습자 텍스트가 날아갈 수 있다.
- `session-review-tabs`: role=tab을 쓰지만 화살표 키 이동·aria-controls·tabpanel 연결이 없어 ARIA 탭 패턴 접근성이 불완전하다.
- `session-review-btn-retry-evaluation`: displayEvaluationRetryError가 실제 오류 detail을 버리고 항상 동일한 일반 문구만 보여줘 실패 원인 파악이 불가능하다. 노출 조건이 '평가 실패'·'종료됨' 같은 한국어 표시 문자열 일치에 결합돼 있어 깨지기 쉽다.
- `session-review-btn-share`: clipboard.writeText 실패 시에도 catch에서 'copied' 상태로 표시해 실제 복사 실패를 숨긴다. api에 있는 공유 해지(DELETE /share)는 UI에 노출되지 않아 만든 링크를 화면에서 되돌릴 수 없다. 활성 조건이 표시 문자열 '종료됨' 일치에 의존한다.
- `session-review-share-note`: 공유 URL이 일반 텍스트로만 표시되고 앵커 링크나 재복사 버튼이 없다.
- `session-review-turn-jump`: 필터 해제 직후 requestAnimationFrame 1회로 scrollIntoView를 호출해 React 리렌더 전이면 대상 ref가 아직 없어 스크롤이 무시될 수 있는 경합이 있다.
- `session-review-transcript-display`: 비언어 칩의 상세 내용이 title 속성 툴팁에 의존해 키보드·터치 사용자에게 전달되지 않는다.
- `session-review-worksheet-edit`: worksheet prop이 바뀔 때마다 초안·dirty가 무조건 리셋되므로 백그라운드 폴링/재로딩과 겹치면 저장 전 입력이 소실된다. 이탈 시 미저장 경고(beforeunload/라우트 블록)도 없다.
- `session-review-worksheet-save`: 저장 실패 시 '워크시트를 저장하지 못했습니다' 고정 문구만 표시하고 서버 오류 상세를 버린다.
- `session-review-worksheet-evidence-jump`: evidence 배열의 첫 항목만 노출되어 두 번째 이후 근거는 접근 불가다.
- `session-review-prepost-input`: 저장된 값을 비우는 것은 영구히 불가능하며(빈칸이 invalid 처리), invalid 칸이 하나라도 있으면 다른 유효한 변경까지 통째로 저장이 막힌다.
- `session-review-prepost-save`: 키별 순차 PUT 중 하나가 실패하면 일부만 저장된 채 일반 오류 문구만 표시되고 어떤 항목이 저장됐는지 재동기화되지 않는다.
- `session-review-teacher-save-viewed`: 이미 closed인 검토에 메모 저장을 누르면 status가 viewed로 덮여 검토 완료 상태가 되돌려질 수 있다(닫힘 여부를 보존하지 않음).
- `session-review-teacher-close`: 한 번 closed로 저장하면 화면에서 다시 여는(viewed로 되돌리는) 명시적 수단이 없다(메모 저장 부작용으로만 가능).
- `session-review-teacher-worksheet-note`: 이 textarea만 name·autoComplete 속성이 없어 다른 입력과 마크업 일관성이 없다.
- `session-review-teacher-worksheet-reject`: 승인/수정요청/반려에 확인 다이얼로그가 없어 오클릭 한 번으로 결정이 즉시 저장된다.
## 5. 교수자 콘솔 (professor)
- [x] `professor-guard-require-auth-teacher` RequireAuth(teacher) 라우트 가드 — 미인증은 /login, teacher 권한이 없으면 자기 역할 홈으로 리다이렉트한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-guard-pending-approval` PendingApprovalGate 리다이렉트 — 미승인 계정이 /teach 계열에 오면 /pending으로 보내고 승인 계정이 /pending에 오면 되돌린다 · 검증: 신규 spec 필요
- [x] `professor-guard-onboarding` OnboardingGate 리다이렉트 — 온보딩 미완료 사용자가 /teach 계열에 오면 /onboarding으로 보낸다 · 검증: 신규 spec 필요
- [x] `professor-dashboard-initial-load` 교수자 대시보드 초기 로드 — 검토 대기·최근 세션·안전 알림·학습자 성장 데이터를 불러와 로딩/오류/갱신 시각을 관리한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-dashboard-error-banner` 대시보드 오류 배너 — 로드 실패 시 role=alert 배너에 오류 메시지를 표시한다 · 검증: 신규 spec 필요
- [x] `professor-persona-review-initial-load` 페르소나 검수 큐 초기 로드 — 검수 대기 페르소나를 불러오며 404는 빈 큐로, 5xx만 오류 카드로 처리한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-header-refresh` 새로고침 버튼 — 대시보드와 뷰별 데이터(검수 큐 또는 학습자 분석)를 함께 재조회하고 진행 중에는 비활성화된다 · 검증: 신규 spec 필요
- [x] `professor-open-first-pending-review` 첫 검토 열기 버튼 — 검토 대기가 있을 때만 헤더에 노출되어 첫 번째 회기의 교수자 리뷰로 이동한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `professor-nav-to-analysis` 학생 분석 이동 버튼 — 콘솔 헤더에서 /teach/analysis로 이동한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-nav-to-console` 콘솔로 이동 버튼 — 분석 뷰 헤더에서 /teach 콘솔로 되돌아간다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `professor-triage-status-strip` 검토 큐 상태 스트립 — 코호트 라벨·마지막 갱신 시각·리뷰 대기/누적 회기 수를 role=status로 표시한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `professor-kpi-cards` KPI 카드 6종 — 검토 대기·위기 알림·페르소나 검수·최근 세션·전체 학생·활성 세션 수를 표시 전용 카드로 요약한다 · 검증: 신규 spec 필요
- [x] `professor-pending-review-list` 검토 대기 회기 목록 — 종료됐지만 닫지 않은 회기를 행 버튼으로 나열하고 클릭 시 교수자 리뷰로 이동한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-safety-alert-list` 위기 알림(109) 목록 — safety_alerts를 학습자·페르소나·위험도·발생시각·안내 자원과 함께 나열한다 · 검증: 기존 e2e/session-persistence.spec.ts
- [x] `professor-persona-review-approve` 페르소나 검수 승인 버튼 — review 상태 페르소나에만 노출되어 approve 결정을 저장하고 결과에 따라 행 갱신 또는 큐 제거를 한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-persona-review-open-studio` 스튜디오에서 검수 버튼 — `/teach/personas?draft={id}`로 이동해 스튜디오에서 상세 검수를 잇는다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `professor-persona-review-error-retry` 검수 큐 오류 카드 + 다시 시도 — 로드·결정 실패 시 role=alert 카드와 재조회 버튼을 제공한다 · 검증: 신규 spec 필요
- [x] `professor-persona-review-empty-loading` 검수 큐 로딩/빈 상태 — 로딩 중 '검수 큐 확인 중', 비면 '검수 대기 페르소나 없음'을 표시한다 · 검증: 신규 spec 필요
- [x] `professor-open-persona-studio` 페르소나 스튜디오 열기 버튼 — 콘텐츠 저작실 카드에서 초안 건수 메타와 함께 /teach/personas로 이동한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `professor-recent-session-table` 최근 회기 기록 테이블 — 최근 세션을 행 버튼으로 나열하고 종료 여부에 따라 상세 리뷰/진행 기록 라벨로 리뷰 화면에 진입시킨다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-analysis-learner-search` 학습자 검색 입력 — 학습자 라벨·ID·추이·기법 태그를 합친 문자열에 부분 일치로 실시간 필터링한다 · 검증: 기존 e2e/teacher.spec.ts · 베이스라인 215/215 GREEN
- [x] `professor-analysis-sort-segment` 학습자 정렬 세그먼트 (최근순/회기순/점수순/변화순) — 네 기준 중 하나로 학습자 목록을 재정렬한다 · 검증: 신규 spec 필요
- [x] `professor-analysis-table-states` 학습자 테이블 로딩/빈/검색결과없음 상태 — 데이터 없음·로딩·검색 결과 없음을 각각 다른 문구로 구분한다 · 검증: 신규 spec 필요
- [x] `professor-analysis-row-expand` 학습자 행 펼침 토글 — aria-expanded 버튼으로 회기별 스파크바·점수 변화·기법 태그 요약을 한 행씩 펼친다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-analysis-open-learner-detail` 학습자 상세 진입 (이름 버튼 · 상세 보기 버튼) — `?learner={id}` 쿼리를 설정해 드릴다운 화면으로 전환한다(URL 공유·뒤로가기 가능) · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-analysis-detail-load` 학습자 상세 분석 로드 — learner 파라미터 변경마다 상세를 조회하고 로딩·오류·미선택 상태를 구분하며 낡은 응답을 무시한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `professor-analysis-back-to-list` 전체 목록 복귀 버튼 — learner 쿼리를 지워 검색 테이블로 돌아간다 · 검증: 신규 spec 필요
- [x] `professor-analysis-invalid-learner-redirect` 존재하지 않는 학습자 파라미터 자동 복귀 — learner_growth에 없는 ID면 쿼리를 지워 전체 목록으로 되돌린다 · 검증: 신규 spec 필요
- [x] `professor-analysis-depth-strip` 분석 깊이 표시 스트립 — 현재 드릴다운 깊이(1 목록 → 2 상세 → 3 리뷰)를 표시 전용으로 안내한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `professor-analysis-summary-metrics` 학습자 상세 요약 메트릭 — 전체 회기·완료/진행·검토 대기·최근 적절성·변화·평균 라포 6개 지표를 상단에 요약한다 · 검증: 기존(부분) e2e/layout-visual-gate.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `professor-analysis-tabs` 상세 분석 탭 (페르소나별 회기/추이/전체 회기/단계 분석) — 4개 탭으로 콘텐츠를 전환하고 각 탭에 건수 배지를 표시한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `professor-analysis-persona-group-drill` 페르소나별 회기 그룹 아코디언 — 페르소나 요약 그룹 버튼을 눌러 그룹 내 회기 목록을 한 번에 하나씩 펼친다 · 검증: 기존 e2e/layout-visual-gate.spec.ts
- [x] `professor-analysis-session-row-open` 회기 행 열기 (페르소나 탭·전체 회기 탭 공용) — 회기 요약 행 버튼 클릭 시 교수자 리뷰 화면으로 이동한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `professor-analysis-trend-chart` 추이 탭 회기별 막대 차트 — 회기별 점수를 막대로 그리고 title 툴팁에 페르소나·회기·점수를 담는다 · 검증: 신규 spec 필요
- [x] `professor-analysis-stage-breakdown` 단계 분석 탭 — 상담 단계별 회기 수와 턴 수를 카드 목록으로 표시한다 · 검증: 신규 spec 필요
- [x] `professor-analysis-list-reuses-dashboard` 학습자 목록 데이터 소스 — 분석 뷰 검색 테이블이 별도 API 없이 teacher/dashboard의 learner_growth를 재사용한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
**의심 결함(에이전트 리포트)**:
- `professor-dashboard-error-banner`: 배너 자체에 재시도 버튼이 없음 — 헤더의 새로고침 버튼까지 시선 이동 필요. 오류 상태에서도 아래 큐 섹션들이 빈 상태 문구로 그대로 렌더되어 '데이터 없음'과 '로드 실패'가 혼동될 수 있음.
- `professor-kpi-cards`: KPI 카드가 해당 섹션으로 스크롤/이동하는 링크가 아니어서 숫자만 보이는 죽은 UI — 특히 '위기 알림' 같은 긴급 지표도 클릭 동작이 없음.
- `professor-safety-alert-list`: 표시 전용이라 알림 확인(acknowledge)·해결 처리 액션이 전혀 없고, session_id를 보여주면서도 해당 회기 리뷰로 이동하는 링크가 없어 위기 대응 동선이 끊김.
- `professor-persona-review-approve`: 승인 실패 시 setPersonaReviewError로 패널 전체가 에러 카드로 대체되어 검수 목록이 통째로 사라짐(단건 실패가 전역 오류로 승격). 또한 busy 비교가 정확히 '해당 persona:action'만이라 다른 행의 승인 버튼은 여전히 눌러져 동시 다발 요청 가능. 확인(confirm) 단계 없이 즉시 승인됨.
- `professor-recent-session-table`: 테이블이 div+button 구조이고 헤더 행이 aria-hidden이라 스크린리더는 열 제목 없이 셀 텍스트만 듣게 됨 (data-label 속성은 시각용). 진짜 table/grid 시맨틱 부재.
- `professor-analysis-sort-segment`: 활성 표시가 CSS 클래스(is-active)뿐이고 aria-pressed/aria-current가 없어 보조기기에서 현재 정렬 기준을 알 수 없음.
- `professor-analysis-detail-load`: 상세 로드 오류 카드에 재시도 버튼이 없음 — 헤더 새로고침으로만 복구 가능.
- `professor-analysis-invalid-learner-redirect`: learner_growth가 아직 로드되지 않은 짧은 순간에는 판정을 보류하지만, 대시보드 로드가 실패하면 learnerGrowth가 빈 배열로 남아 유효한 딥링크도 상세 제목이 '학습자 이력' 폴백으로 표시됨.
- `professor-analysis-depth-strip`: nav 요소지만 각 단계가 클릭 불가한 span이라 브레드크럼처럼 보이면서 동작하지 않음 — 1단계 클릭으로 목록 복귀를 기대하는 사용자에게 혼란.
- `professor-analysis-tabs`: role=tab/aria-selected는 있으나 tabpanel 연결(aria-controls/id)과 화살표 키 로빙 탭인덱스가 없어 WAI-ARIA 탭 패턴 미완성.
- `professor-analysis-trend-chart`: 차트 정보가 title 속성 툴팁에만 있어 키보드/터치/스크린리더로는 개별 막대 값을 알 수 없음.
- `professor-analysis-stage-breakdown`: 다른 탭과 달리 stage_breakdown이 빈 배열일 때 EmptyState가 없어 제목만 있고 내용이 없는 화면이 됨.
## 6. 페르소나 스튜디오 (persona-studio)
- [x] `persona-studio-guard-require-auth` RequireAuth 라우트 가드 (teacher/admin) — 미인증은 /login, teacher/admin이 아니면 자기 역할 홈으로 보낸다 · 검증: 신규 spec 필요
- [x] `persona-studio-guard-pending-approval` PendingApprovalGate 가드 — 미승인 사용자는 /pending으로 리다이렉트한다 · 검증: 신규 spec 필요
- [x] `persona-studio-guard-onboarding` OnboardingGate 가드 — 온보딩 미완료 사용자는 /onboarding으로 리다이렉트한다 · 검증: 신규 spec 필요
- [x] `persona-studio-initial-load` 초기 데이터 로드 (카탈로그·검수 큐·교수 대시보드) — 3개 API를 병렬 호출하며 카탈로그 실패만 전체 오류, 검수 큐 404는 빈 목록, 대시보드 실패는 경고로 처리한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-load-error-banner` 로드 오류 배너 — 카탈로그 로드 실패 시 role=alert 섹션에 오류 메시지를 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-dashboard-degraded-note` 학습 지표 실패 시 부분 동작 안내 — 대시보드 호출 실패 시 경고 노트만 띄우고 카탈로그·저작 기능은 계속 쓰게 한다 · 검증: 신규 spec 필요
- [x] `persona-studio-status-message-line` 폼 오류/성공 메시지 라인 — formError가 있으면 role=alert 오류를, 없으면 성공/안내 문장을 헤더 아래에 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-refresh-button` 새로고침 버튼 — 카탈로그·검수 큐·대시보드를 다시 호출하며 로딩·busy 중에는 비활성화된다 · 검증: 신규 spec 필요
- [x] `persona-studio-workspace-tabs` 워크스페이스 탭 (대시보드/카탈로그/페르소나) — view 쿼리 기반으로 탭을 전환하며 나머지 쿼리 파라미터를 모두 제거한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-create-open` 새로운 페르소나 만들기 버튼 — 초안 상태를 초기화하고 localStorage 임시본이 있으면 복원 메시지와 함께 되살린 뒤 작성 단계로 이동한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-dash-review-more` 검수 현황 더보기 버튼 — `view=personas&queue=review`로 이동해 검수 대기 목록을 연다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-dash-usage-row` 페르소나별 학습 현황 행 클릭 — 공개 페르소나 상위 8개 행을 클릭하면 승인본 상세로 이동한다 · 검증: 신규 spec 필요
- [x] `persona-studio-dash-usage-empty` 공개 페르소나 없음 빈 상태 — 카탈로그가 비면 '공개 페르소나 없음' 빈 상태를 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-catalog-view-all` 전체 페르소나 보기 버튼 — 카탈로그 화면에서 `view=personas` 목록으로 이동한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-catalog-row` 공개본 상태 목록 행 클릭 — 공개 페르소나 행을 클릭하면 승인본 상세로 이동한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `persona-studio-catalog-empty-distribution` 이론 분포 빈 상태 — 공개 페르소나가 없으면 이론 분포 대신 '분포 자료 없음'을 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-list-subtabs` 목록 서브탭 (전체 페르소나/검수 현황) — queue 파라미터를 all/review로 바꾸고 열린 상세를 닫으며 건수를 표시한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-list-search` 페르소나 검색 입력 — 코드·이름·이론·상태 라벨 문자열에 ko-KR 소문자 부분일치로 필터링한다 · 검증: 신규 spec 필요
- [x] `persona-studio-list-origin-filter` 구분 필터 셀렉트 (전체/시스템/새로 만든) — P1~P11·seed 출처는 시스템, 나머지는 커스텀으로 분류해 필터링한다 · 검증: 신규 spec 필요
- [x] `persona-studio-list-row-open-detail` 목록 행 식별자/상세 버튼 — 행의 코드·이름 또는 상세 버튼으로 `persona={key}` 상세 화면을 연다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-list-approved-revise` 공개본 수정 버튼 (새 버전 초안 생성) — 승인본에서 revision 초안을 만들어 편집기로 불러오고 승인 전까지 공개본을 유지한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-list-approved-archive` 공개본 삭제(보관) 버튼 — confirm 후 DELETE로 보관 처리하고 같은 대상 편집 중이면 상태를 초기화한 뒤 목록을 새로고침한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-list-draft-edit` 초안 행 수정 버튼 — `mode=edit&draft={id}`로 이동해 초안 상세를 편집 폼에 자동 로드한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-list-empty` 목록 필터 결과 빈 상태 — 검색·필터 결과가 없으면 '조건에 맞는 페르소나 없음'을 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-review-card-detail` 검수 카드 상세 버튼 — `persona=draft:{id}`로 이동해 해당 초안 상세를 연다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-review-card-edit` 검수 카드 수정 버튼 — 해당 초안을 mode=edit 편집기로 연다 · 검증: 신규 spec 필요
- [x] `persona-studio-review-card-approve` 검수 카드 승인 버튼 — approve 결정으로 공개본으로 승격하고 편집 상태를 초기화한 뒤 목록을 새로고침한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-review-card-reject` 검수 카드 반려 버튼 — reject 결정을 저장하며 status가 review가 아니거나 busy면 비활성화된다 · 검증: 신규 spec 필요
- [x] `persona-studio-review-empty` 검수 대기 없음 빈 상태 — review/draft 항목이 없으면 '검수 대기 없음'을 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-detail-back` 상세 → 목록으로 버튼 — persona·detailTab 쿼리를 제거해 목록으로 돌아간다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-detail-tabs` 상세 서브탭 (소개·정보/학습 현황/설정 내용) — detailTab 쿼리로 상세 화면 3개 탭을 전환한다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-detail-draft-fetch` 초안 상세 자동 조회 — persona 키가 draft:면 초안 상세를 조회해 주호소·핵심 신념·민감 영역·출처를 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-detail-revise-button` 상세 헤더 수정 버튼 — 승인본이면 revision 초안을 만들어, 초안이면 바로 편집기로 이동한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-detail-learning-empty` 학습 기록 없음 빈 상태 — 평가된 회기 궤적이 없으면 빈 상태를 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-detail-notfound` 상세 대상 미존재 빈 상태 — persona 키가 목록에 없으면 '페르소나를 찾지 못했습니다'를 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-author-stepper` 저작 4단계 스테퍼 (자료/초안/설정/검토) — step 쿼리로 단계를 이동하며 검토 단계 진입 시 프롬프트 미리보기로 자동 전환한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-author-back-to-list` 저작 화면 목록으로 버튼 — `view=personas`로 돌아가 저작 모드를 종료하며 busy 중에는 비활성화된다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-author-rail-draft-load` 좌측 레일 초안 목록 클릭 로드 — 검수 큐 초안을 클릭하면 상세를 불러와 현재 편집 폼을 교체한다 · 검증: 신규 spec 필요 · 현행 설계 특성화 GREEN(저작 모드 레일 숨김 — 결함 로그 #14 소유자 확인)
- [x] `persona-studio-author-rail-approved-actions` 좌측 레일 공개본 수정/삭제 버튼 — 공개본별로 revision 초안 생성 또는 confirm 후 보관 처리를 수행한다 · 검증: 신규 spec 필요 · 현행 설계 특성화 GREEN(저작 모드 레일 숨김 — 결함 로그 #14 소유자 확인)
- [x] `persona-studio-author-empty-states` 저작 레일/인스펙터 빈 상태 — 초안·공개본·결정 대상이 없을 때 각각의 빈 문구를 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-source-dropzone` 자료 파일 드래그&드롭 업로드 — 드롭된 첫 파일만 처리해 엑셀은 서버 변환, 텍스트는 브라우저에서 읽어 본문에 채운다 · 검증: 신규 spec 필요
- [x] `persona-studio-source-file-picker` 파일 선택 버튼 + 숨김 file input — 엑셀은 multipart 업로드로 표→텍스트 변환·KB 등록하고 그 외는 본문에 읽어 넣는다 · 검증: 신규 spec 필요
- [x] `persona-studio-source-kind-select` 자료 종류 셀렉트 — client_record/textbook_guide/mixed_notes 중 근거 자료 종류를 선택해 요청에 함께 보낸다 · 검증: 신규 spec 필요
- [x] `persona-studio-source-name-input` 파일/자료명 입력 — 등록될 근거 문서의 파일명·제목으로 쓰이며 비우면 기본값이 적용된다 · 검증: 신규 spec 필요
- [x] `persona-studio-generation-goal-input` 생성 목표 입력 — 초안 생성 요청의 generation_goal과 근거 문서 source_note로 전달된다 · 검증: 신규 spec 필요
- [x] `persona-studio-source-text-area` 자료 본문 붙여넣기 textarea — 내담자 기록·교재 기준을 붙여넣으면 생성 시 KB 근거 문서로 먼저 등록된다(20자 이상) · 검증: 신규 spec 필요
- [x] `persona-studio-generate-draft` KB 등록 후 생성 버튼 — 붙여넣은 텍스트를 근거 문서로 등록한 뒤 RAG 초안 생성을 호출하고 결과·마스킹·근거 청크를 인스펙터에 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-studio-tabs` 저작 섹션 탭 7종 (개요/임상/저항/회기/말투/안전/프롬프트) — role=tablist로 편집 섹션을 전환하며 섹션별 작성 가이드를 함께 보여준다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-field-code` 코드 입력 (자동 제안·중복 검사) — 대문자로 정규화하고 비면 다음 P{n}을 제안하며 카탈로그·검수 큐와 중복이면 검증 오류를 낸다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-field-display-name` 표시 이름 입력 — 표시 이름을 입력하며 비면 저장·검수 요청 모두 검증 오류가 된다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-field-difficulty` 난이도 셀렉트 (easy/moderate/hard) — 페르소나 난이도를 선택한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-field-synthetic` 합성 여부 셀렉트 (synthetic/clinical source) — 합성 초안인지 실제 임상 근거 기반인지 is_synthetic 플래그를 설정한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-theory-toggle` 대상 이론 토글 (humanistic/cbt/integrative) — 세그먼트 버튼으로 이론을 다중 선택·해제하며 마지막 하나를 해제하면 humanistic으로 되돌린다 · 검증: 신규 spec 필요
- [x] `persona-studio-fields-demographics` 인구통계 입력 4종 (연령대/성별·젠더/역할·학년/맥락) — demographics 객체의 네 키를 자유 텍스트로 편집한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-fields-presenting` 주호소·첫 회기 입장 발화 textarea — presenting.complaint와 first_session_opening을 편집하며 주호소는 검수 요청 시 필수다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-field-provenance` 출처(source_provenance) 입력 — 작성 근거·출처를 기록하며 비면 경고로 표시된다 · 검증: 신규 spec 필요
- [x] `persona-studio-fields-clinical` 임상 탭 입력 (가족/학교·직장/관계/촉발 사건/강점 + CCD 5종 + 증상 메모) — history 5개와 CCD·dsm5 메모를 편집하며 핵심신념은 검수 요청 시 필수다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-fields-resistance-numbers` 저항 탭 숫자 입력 16종 (저항 5·Big5 5·정서 기저선 5·자살사고 단계) — 0~1 파라미터와 자살사고 단계를 number 입력으로 편집한다 · 검증: 신규 spec 필요
- [x] `persona-studio-fields-scenario` 회기 탭 입력 (회기별 시나리오 리스트/표면화 조건/추가 프롬프트 계약) — session_scenarios 리스트와 surface·prompt_contract 장문을 편집한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-fields-speech` 말투 탭 입력 (어조/문장 길이/존댓말/군말·말버릇·비언어 단서) — speech_style의 셀렉트와 세 리스트를 편집한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-fields-safety` 안전 탭 입력 (역린 민감 영역/상담자 금기/역린 반응/내담자 금기어) — triggers의 리스트와 reaction 장문을 편집하며 sore_spots는 검수 요청 시 필수다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-listfield-add-remove` 리스트 필드 공통 항목 추가/삭제 — 모든 ListField에서 빈 행 추가와 X 버튼 삭제를 제공하고 저장 시 빈 항목을 거른다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-prompt-preview` 프롬프트 미리보기 패널 (읽기 전용) — 구조화 데이터를 8개 섹션 런타임 계약으로 렌더하고 빈 섹션은 '작성된 항목 없음'으로 표시한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-author-prev-next` 이전/다음 단계 버튼 — step 쿼리를 앞뒤로 옮기며 마지막 단계에서는 검수 요청 버튼으로 대체된다 · 검증: 기존(부분) e2e/teacher.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `persona-studio-save-progress` 중간 저장 버튼 — 작성 내용을 localStorage에 항상 저장하고 최소 검증을 통과하면 서버 초안으로도 생성·갱신한다 · 검증: 신규 spec 필요
- [x] `persona-studio-submit-review` 검수 요청 버튼 — 전체 검증 통과 시 submit_for_review로 저장해 상태를 review로 올리고 localStorage 임시본을 삭제한다 · 검증: 기존 e2e/teacher.spec.ts
- [x] `persona-studio-inspector-validation` 인스펙터 검증 체크리스트 — 필수 오류·경고를 실시간으로 나열하고 모두 통과하면 완료 배지를 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-inspector-evidence` 인스펙터 생성 근거 패널 — 생성 요약·PII 마스킹·경고·등록 근거 문서·상위 3개 근거 청크를 표시한다 · 검증: 신규 spec 필요
- [x] `persona-studio-inspector-review-actions` 인스펙터 검수 큐 편집/반려/승인 버튼 — 저작 화면 우측에서 초안을 편집기로 로드하거나 즉시 승인·반려 결정을 보낸다 · 검증: 신규 spec 필요
- [x] `persona-studio-draft-autoload-param` `?draft=` 파라미터 초안 자동 로드 — draft 파라미터로 진입하면 로드 완료 후 해당 초안을 한 번 자동으로 편집 폼에 채운다 · 검증: 신규 spec 필요
- [x] `persona-studio-loading-busy-states` busy 상태별 버튼 라벨/비활성화 — load/save/submit/decide/generate/archive 작업 중 관련 버튼을 진행 라벨로 바꾸고 비활성화한다 · 검증: 신규 spec 필요
**의심 결함(에이전트 리포트)**:
- `persona-studio-load-error-banner`: 오류 배너에 재시도 버튼이 없어 헤더의 새로고침 버튼을 찾아야 재시도 가능
- `persona-studio-status-message-line`: formError가 message를 항상 가리므로 직전 성공 안내가 오류 발생 시 소실됨
- `persona-studio-workspace-tabs`: 저작(mode=create/edit) 중 탭 클릭 시 확인 없이 편집 화면을 이탈해 미저장 서버 반영분이 사라질 수 있음(localStorage 임시본만 남음)
- `persona-studio-create-open`: 복원된 임시본은 검수 요청 성공 때만 삭제되므로 오래된 초안이 새 작성 세션에 계속 되살아날 수 있음
- `persona-studio-dash-usage-row`: role=table/row 구조인데 행 전체가 button이고 cell 역할이 없어 스크린리더 표 탐색이 어긋남
- `persona-studio-list-origin-filter`: 검색어·필터가 쿼리스트링에 저장되지 않아 상세 진입 후 돌아오면 초기화됨
- `persona-studio-list-approved-revise`: persona_id가 없는 시드 공개본은 버튼이 disabled이나 이유 툴팁이 없어 사용자가 원인을 알 수 없음
- `persona-studio-list-approved-archive`: 네이티브 window.confirm 사용으로 앱 디자인·접근성과 불일치하고 E2E에서 다이얼로그 처리가 필요
- `persona-studio-detail-draft-fetch`: 조회 실패를 catch에서 조용히 무시(null 처리)해 사용자는 작성 정보 섹션이 왜 안 보이는지 알 수 없음
- `persona-studio-detail-notfound`: 빈 상태에 목록으로 돌아가는 버튼이 없어 사용자가 직접 URL/탭을 조작해야 함
- `persona-studio-author-stepper`: 단계 이동에 완료 조건 검증이 없어 자료 없이도 검토 단계로 건너뛸 수 있음(의도된 자유 이동일 수 있음)
- `persona-studio-author-back-to-list`: 미저장 변경이 있어도 확인 없이 이탈함(localStorage 임시본은 중간 저장을 눌렀을 때만 존재)
- `persona-studio-author-rail-draft-load`: 현재 작성 중이던 내용을 확인 없이 덮어씀
- `persona-studio-source-dropzone`: 여러 파일을 드롭해도 첫 파일만 조용히 처리되고 나머지는 무시됨
- `persona-studio-source-file-picker`: accept에 .xls가 빠져 있는데 코드 분기는 .xls도 처리함(파일 선택 대화상자에서 .xls가 걸러짐); 텍스트 분기는 확장자 검증 없이 아무 파일이나 text()로 읽음
- `persona-studio-theory-toggle`: 마지막 항목 해제 시 조용히 humanistic으로 바뀌어 사용자가 의도치 않은 값을 가질 수 있음(안내 없음)
- `persona-studio-fields-resistance-numbers`: min/max가 HTML 속성뿐이라 직접 타이핑하면 범위 밖 값(예: 7, -3)이 상태에 그대로 저장되고 JS 클램핑·검증이 없음
- `persona-studio-generate-draft`: 재클릭 시 같은 붙여넣기 텍스트가 매번 새 근거 문서로 중복 등록됨; 생성 성공 시 기존 편집 내용을 확인 없이 덮어씀
- `persona-studio-save-progress`: localStorage에는 sourceText 등 원문 자료가 평문 저장됨(내담자 실제 기록일 경우 브라우저에 PII 잔존)
- `persona-studio-inspector-review-actions`: 검수 보드와 달리 status!=='review'(작성 중 draft)여도 승인/반려 버튼이 활성화되어 UI 가드가 불일치함(서버 거부에 의존)
- `persona-studio-draft-autoload-param`: Professor 페이지는 mode 없이 `?draft=`만 붙여 이동하는데 자동 로드는 isAuthoring(mode=create/edit)일 때만 동작해 해당 진입 경로에서 초안이 로드되지 않음
## 7. 관리자 콘솔 (admin)
- [x] `admin-guard-require-auth` RequireAuth admin 가드 — 미인증은 /login, admin·superAdmin·adminAccess가 없으면 자기 역할 홈으로 리다이렉트한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-guard-pending-approval` PendingApprovalGate 승인 대기 가드 — 미승인 사용자가 /admin 계열에 오면 /pending으로 보내고 승인 사용자가 /pending에 있으면 되돌린다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-guard-onboarding-bypass` OnboardingGate admin 우회 — 온보딩 미완료여도 admin 접근 권한이 있으면 /admin 경로는 그대로 통과시킨다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-error-boundary` AdminErrorBoundary 렌더 예외 진단 — 렌더 예외 시 빈 화면 대신 section·path·error·componentStack 진단 패널을 띄우고 섹션 변경 시 리셋한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-blank-content-probe` 빈 본문 감지 프로브 — 마운트 800ms 후 본문 텍스트·높이·가시 노드를 검사해 비어 있으면 진단 패널을 표시한다 · 검증: 기존 e2e/public-admin-visual.spec.ts
- [x] `admin-data-diagnostics-panel` 관리자 데이터 계약 진단 패널 — users/tickets 응답 정규화 중 생긴 경고를 최대 8건까지 나열하고 초과분은 개수로 요약한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-unknown-section-panel` 알 수 없는 섹션 폴백 패널 — 정의되지 않은 섹션이 전달되면 섹션·경로·asset 진단 패널을 표시한다 · 검증: 신규 spec 필요 · 도달 불가 방어 코드(라우터가 섹션을 하드코딩, 미정의 경로는 루트 회수) — E2E 불가, 코드 확인으로 종결
- [x] `admin-overview-refresh-button` 운영 홈 새로고침 버튼 — 헬스·사용자·사용량·업타임·티켓 5개 API를 병렬 재조회하고 로딩 중 비활성화한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-overview-status-banner` 서비스 상태 배너 — health 상태에 따라 색상·문구를 바꾸고 환경·엔진 모드·갱신 시각을 role=status로 표시한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-overview-kpi-cards` 운영 요약 KPI 4종 — 활성 회기·최근 15분 온라인 사용자·등록 사용자 수·헬스 ok/total을 카드로 표시한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-overview-flow-panel` 실시간 교육 흐름 패널 — 학습자 회기 수·온라인 교수자/관리자 수·최근 7일 AI 계량 턴 수를 표시한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-overview-availability-panel` 가용성 패널 — 현재 헬스, 24시간 샘플 정상률(샘플 없음/미연결 구분), 최근 중단 시각을 표시한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-overview-cost-panel` AI 비용 패널 — 7일 비용·예산 상태·사용률 바·평가 캐시 hit-rate·일별 비용 추이를 표시한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-overview-ticket-mini-panel` 운영 티켓 미니 패널 — 미해결·높은 우선순위·24시간 정체 요약과 상위 활성 티켓 2건을 표시한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-overview-services-list` 서비스 리소스 목록 — health.services를 상태 Dot·Badge·detail·load 바로 렌더하고 로딩 스켈레톤과 빈 문구를 갖는다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-overview-health-error` 헬스 조회 실패 InlineError — health 호출 실패 시 오류 메시지를 role=alert 섹션으로 표시한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-tabs` 사용자 관리 탭바(승인/목록/등록/활동) — 대기 건수를 표시한 4개 탭을 role=tablist + aria-selected로 전환한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-users-approval-queue` 가입 승인 대기 큐 — pending 사용자를 접수일 내림차순으로 나열하고 집계 배지·스켈레톤·빈 상태를 제공한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-users-approve-button` 가입 승인 버튼 — 대기 사용자의 account_status를 approved로 PATCH하고 목록·드래프트를 갱신한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-users-suspend-button` 가입 보류 버튼 — 대기 사용자의 account_status를 suspended로 PATCH해 보류 처리한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-readonly-guard` DB 미연결 읽기 전용 경고 — durable이 false면 경고를 띄우고 승인·등록·편집·저장·비활성화 등 쓰기 UI를 모두 비활성화한다 · 검증: 기존(부분) e2e/readiness.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-users-search-input` 사용자 검색 입력 — 이메일·이름·역할·권한·상태·소속·코호트 문자열에 부분 일치로 필터링하고 카운트를 표시한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-table-sort` 사용자 표 컬럼 정렬 — TanStack Table로 8개 헤더를 클릭 정렬(기본 last_seen_at 내림차순)하고 aria-sort를 갱신한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-table-scroll-region` 사용자 표 가로 스크롤 영역 — 작은 화면에서 표를 tabIndex=0, role=region 가로 스크롤 컨테이너로 감싼다 · 검증: 기존 e2e/layout-visual-gate.spec.ts + e2e/admin.spec.ts
- [x] `admin-users-row-cap` 표 40행 렌더 제한 안내 — 상위 40행만 렌더하고 초과분은 검색으로 좁히라는 안내문을 표시한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-edit-name-input` 행 내 표시 이름 편집 — 각 행의 표시 이름을 드래프트로 수정하며 DB 미연결·저장 중에는 비활성화한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-users-edit-role-select` 행 내 역할 셀렉트 — 역할을 드래프트로 변경하되 관리자 옵션은 superAdmin만 선택 가능하다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-users-admin-access-toggle` 관리자 페이지 권한 체크박스 — admin_access를 토글하며 superAdmin이 아니거나 대상이 super_admin이면 비활성화한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-status-select` 행 내 승인 상태 셀렉트 — 승인 대기/승인됨/보류를 드래프트로 바꾸고 현재 서버 값은 옆 배지로 구분한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-users-affiliation-input` 행 내 소속 편집 — 소속 문자열을 드래프트로 수정한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-cohort-input` 행 내 코호트 편집 — 콤마 구분 문자열을 trim·빈값 제거해 cohort_ids 배열로 반영한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-save-button` 행 저장 버튼 — 드래프트가 서버 값과 다를 때만 활성화되어 PATCH하고 응답으로 로컬 상태를 교체한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-users-deactivate-button` 사용자 비활성화 버튼 — DELETE 후 목록·드래프트에서 즉시 제거하며 처리 중 라벨로 중복 클릭을 막는다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-users-register-form` 새 사용자 등록 폼 — 이메일·이름·역할·권한·상태·코호트·소속을 POST하고 성공 시 폼 초기화 후 목록 탭으로 이동한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-users-activity-tab` 활동 요약 탭 — 역할 분포를 ProgressBar로, last_seen 상위 8명의 최근 활동을 표로 표시한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-users-states` 사용자 로딩/빈/오류 상태 — 스켈레톤, InlineError와 재시도 안내, 검색 0건·등록 0건 문구를 구분해 표시한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-access-tabs` 접근 권한 탭바(역할/그룹/매트릭스) — 3개 탭을 role=tablist로 전환한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `admin-access-roles-cards` 역할 정책 카드 — 관리자/교수자/학습자 3개 역할의 범위·권한 칩·위험 문구를 하드코딩 상수로 표시한다 · 검증: 기존(부분) e2e/public-admin-visual.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `admin-access-groups-cards` 그룹 정책 카드 — 운영자/학과 관리자/슈퍼바이저 그룹의 범위와 접근 칩을 '정책 초안' 배지와 함께 표시한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-access-matrix-table` 권한 매트릭스 표 — 리소스별 역할 접근 수준을 정적 5행 표로 표시한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-tickets-refresh-button` 티켓 새로고침 버튼 — 현재 필터 조합으로 티켓 목록을 재조회하고 로딩 중 비활성화한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-search-input` 티켓 검색 입력 — 제목·본문·신고자·경로를 서버 search 파라미터로 필터링하며 값 변경 시 자동 재조회한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-status-filter` 상태 필터 셀렉트 — 전체/미해결/분류됨/처리 중/해결/종결을 status 파라미터로 서버 필터링한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-category-filter` 카테고리 필터 셀렉트 — 계정·세션·음성·콘텐츠·안전·기타를 category 파라미터로 서버 필터링한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-priority-filter` 우선순위 필터 셀렉트 — 긴급/높음/보통/낮음을 priority 파라미터로 서버 필터링한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-assigned-group-input` 담당 그룹 필터 입력 — assigned_group 파라미터로 담당 그룹을 서버 필터링한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-stale-toggle` 정체만 체크박스 — stale_only=true로 24시간 이상 미변경 티켓만 조회한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-clear-filters-button` 필터 초기화 버튼 — 모든 필터를 기본값으로 되돌리며 필터가 없으면 비활성화된다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-category-queue-chips` 카테고리 큐 칩 버튼 — 건수가 있는 카테고리를 칩으로 나열하고 클릭 시 해당 필터를 적용하며 활성 칩을 강조한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-kpi-cards` 티켓 요약 KPI 4종 — 전체 접수·미해결·높은 우선순위·정체 건수를 summary 기반 카드로 표시한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-tickets-active-list` 미해결 티켓 큐 목록 — 활성 티켓 최대 24건을 카테고리·신고자·중복 관계·처리 이력·배지와 함께 렌더한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-ticket-inprogress-button` 티켓 '처리 중' 버튼 — 상태를 in_progress로 PATCH하고 pendingRef로 중복 클릭을 즉시 차단한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-ticket-resolve-button` 티켓 '해결' 버튼 — 상태를 resolved로 PATCH하고 성공 시 큐에서 이력으로 내린다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-ticket-link-duplicate-button` 중복 후보 '연결' 버튼 — 중복 후보가 있을 때만 노출되어 parent_ticket_id를 후보로 PATCH해 연결한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-ticket-unlink-duplicate-button` 중복 연결 '해제' 버튼 — parent_ticket_id를 빈 값으로 PATCH해 중복 연결을 해제한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
- [x] `admin-tickets-resolved-history` 최근 해결 이력 섹션 — 해결·종결 티켓 최대 6건을 읽기 전용 '이력' 카드로 표시한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-tickets-states` 티켓 로딩/빈/오류 상태 — 최초 로딩·활성 0건 빈 상태·실패 InlineError를 구분해 표시한다 · 검증: 신규 spec 필요 · 신규 spec GREEN(full-sweep-admin)
**의심 결함(에이전트 리포트)**:
- `admin-data-diagnostics-panel`: users는 필드 단위 정규화가 있지만 tickets 배열은 as AdminSupportTicket[] 캐스트만 함 — reporter 등 누락 시 ticket.reporter.email 접근에서 렌더 크래시(에러 바운더리로만 방어)
- `admin-overview-refresh-button`: disabled 조건이 loading||usersLoading||usageLoading뿐이라 uptime·tickets 로딩 중에는 중복 클릭 가능
- `admin-users-tabs`: role=tab이지만 화살표 키 내비게이션·aria-controls·tabpanel 연결이 없어 ARIA 탭 패턴 미완성
- `admin-users-row-cap`: 페이지네이션이 없어 41번째 이후 사용자는 검색 없이는 도달 불가
- `admin-users-status-select`: super_admin 계정의 승인 상태도 일반 관리자가 보류로 바꿀 수 있게 UI가 열려 있음(서버 검증 의존)
- `admin-users-deactivate-button`: 확인 대화상자 없이 한 번의 클릭으로 계정이 비활성화됨(파괴적 작업 컨펌 누락)
- `admin-users-register-form`: 이메일 형식 검증 없이 비어 있지만 않으면 제출됨; 오류는 상단 usersError 배너로만 표시되어 폼 필드와 연결 안 됨
- `admin-users-states`: '새로고침을 눌러 다시 시도하세요' 문구가 있지만 users 탭 화면에는 재시도 버튼이 없음(새로고침 버튼은 overview에만 존재)
- `admin-access-tabs`: users 탭바와 동일하게 키보드 화살표 내비게이션 미구현
- `admin-access-roles-cards`: 화면 스스로 '역할·그룹 저장 API는 아직 분리되지 않았다'고 명시한 정적 화면 — 조작 요소가 전혀 없어 실데이터와 어긋날 수 있음
- `admin-tickets-search-input`: 디바운스가 없어 키 입력마다 GET /admin/tickets가 발사됨(담당 그룹 입력도 동일)
- `admin-tickets-assigned-group-input`: 키 입력마다 즉시 재조회(디바운스 없음)
- `admin-tickets-category-queue-chips`: 활성 칩을 다시 클릭해도 해제(전체로 복귀)되지 않음 — 해제하려면 셀렉트나 초기화 버튼을 써야 함
- `admin-ticket-resolve-button`: 성공 시 로컬 상태 교체와 loadTickets 재조회를 둘 다 수행해 요청이 중복됨(경미)
- `admin-tickets-states`: loadTickets가 시작·성공 시 ticketsError를 초기화하지 않아 한 번 실패하면 이후 조회가 성공해도 오류 배너가 계속 남음(티켓 상태 변경 성공 시에만 지워짐)
## 8. 관리자 AI 설정 (admin-ai)
- [x] `admin-ai-guard-require-auth` RequireAuth(admin) 라우트 가드 — 미인증은 /login, admin 권한이 없으면 자기 역할 홈으로 리다이렉트한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-ai-guard-pending-approval` PendingApprovalGate 리다이렉트 — 미승인 사용자가 /admin/ai에 오면 /pending으로 리다이렉트한다 · 검증: 신규 spec 필요
- [x] `admin-ai-guard-onboarding-bypass` OnboardingGate 관리자 경로 우회 — 온보딩 미완료여도 admin 권한 보유 시 /admin/* 경로를 통과시킨다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-ai-shell-sidebar-nav` AppShell 관리자 사이드바 내비게이션 — navRole=admin 셸로 관리자 메뉴 링크를 제공하고 현재 페이지를 활성 표시한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-ai-initial-load` 초기 데이터 병렬 로드 — usage·health·engine-config·engine-capabilities를 allSettled로 호출해 성공분만 반영하고 실패 메시지는 중복 제거해 배너에 잇는다 · 검증: e2e/admin.spec.ts + e2e/full-sweep-admin-ai.spec.ts
- [x] `admin-ai-refresh-button` 새로고침 버튼 — 사용량·헬스·설정·provider capability를 모두 다시 불러오며 로딩 중 비활성화된다 · 검증: e2e/full-sweep-admin-ai.spec.ts
- [x] `admin-ai-error-alert` 오류 배너(role=alert) — 로드·저장 실패 시 오류 메시지를 role=alert 배너로 띄우고 다음 요청 시작 시 초기화한다 · 검증: 신규 spec 필요
- [x] `admin-ai-aria-busy` 로딩 중 aria-busy 표시 — 페이지 컨테이너에 로딩 동안 aria-busy=true를 설정한다 · 검증: 신규 spec 필요
- [x] `admin-ai-window-toggle` 계량 기간 토글(7/30/90일) — 기간을 바꾸면 usage만 재조회하고 늦게 도착한 응답은 active 플래그로 무시한다 · 검증: 신규 spec 필요
- [x] `admin-ai-source-indicator` 계량 데이터 원천 표시 — durable+database면 '운영 DB 원장'을, 아니면 런타임 임시 집계와 소실 경고를 표시한다 · 검증: 기존 e2e/readiness.spec.ts
- [x] `admin-ai-ledger-cards` 핵심 지표 카드 5종 — 누적 비용·계량 호출·입출력 토큰·호출당 비용을 usage에서 계산해 표시한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `admin-ai-budget-panel` 예산 상태·사용률 트랙 — budget.status를 배지로, used_ratio를 100% 캡 진행 바로 표시하고 disabled면 설정 안내를 보여준다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `admin-ai-coverage-track` DB 계량 커버리지 트랙 — metered_turns/total_turns 비율을 진행 바로 표시하고 미계량 턴 수를 안내한다 · 검증: 신규 spec 필요
- [x] `admin-ai-daily-chart` 일별 비용 막대 차트 — daily_cost를 상대 높이 막대로 렌더하고 날짜·비용 툴팁과 role=img aria-label을 제공한다 · 검증: 신규 spec 필요
- [x] `admin-ai-daily-chart-empty` 일별 차트 빈 상태 — 선택 기간에 계량된 호출이 없으면 안내 문구를 보여준다 · 검증: 신규 spec 필요
- [x] `admin-ai-provider-table` Provider·모델별 사용 원장 테이블 — by_provider 행을 비용 내림차순으로 정렬해 호출·토큰·비용·비중을 표로 보여준다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `admin-ai-provider-table-empty` 모델 원장 빈 상태 — 계량 행이 없으면 '모델별 계량 행이 아직 없습니다.'를 보여준다 · 검증: 신규 spec 필요
- [x] `admin-ai-engine-status-pill` 엔진 런타임 상태 배지 — health.services의 engine 서비스를 정상/중단/제한 운영으로 표시하고 미확인 시 '확인 불가'를 보여준다 · 검증: 기존(부분) e2e/settings.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `admin-ai-engine-provider-select` AI provider 드롭다운(6종) — Claude CLI·Anthropic API·Codex CLI·Agy CLI·OpenAI·Solar를 select로 제공하고, 설치·인증되어 모델을 조회할 수 있는 provider만 저장 가능하다 · 검증: e2e/admin.spec.ts + e2e/full-sweep-admin-ai.spec.ts
- [x] `admin-ai-engine-url-input` 연결 주소 입력 — engine_url을 편집하면 이전 주소의 model catalog를 즉시 무효화하고, 새 주소를 query로 전달한 목록 새로고침이 끝나기 전에는 저장할 수 없다 · 검증: e2e/admin.spec.ts desktop/mobile
- [x] `admin-ai-engine-model-select` 기본 모델 드롭다운 — 선택 provider의 live capability 목록만 표시하고 Codex는 Terra, Agy는 Gemini 3.6 Flash High를 기본 선택한다 · 검증: e2e/admin.spec.ts + e2e/full-sweep-admin-ai.spec.ts
- [x] `admin-ai-engine-effort-select` 추론 강도 드롭다운 — 선택 모델이 실제 지원하는 강도만 표시하고 Codex Terra는 Medium, Agy Flash는 High를 기본 선택한다 · 검증: e2e/admin.spec.ts
- [x] `admin-ai-engine-config-meta` 엔진 설정 메타 정보 — 저장 원천(DB 영구/런타임)·현재 소스·최근 변경 시각·변경자를 dl로 표시한다 · 검증: 신규 spec 필요
- [x] `admin-ai-engine-save-button` 운영 설정 저장 버튼 — provider·URL·모델·추론 강도를 PATCH 저장한다. 서버는 제안된 게이트웨이 capability를 다시 검증하고 성공 후 health를 재조회하며 2.4초간 '저장됨' 배지를 띄운다 · 검증: e2e/admin.spec.ts + e2e/db-persistence.spec.ts
- [x] `admin-ai-engine-loading-empty` 엔진 설정 로딩/실패 빈 상태 — engine-config가 없으면 '불러오는 중입니다' 문구를 표시한다 · 검증: 신규 spec 필요
- [x] `admin-ai-cache-panel` 평가 캐시 효율 패널 — enabled 배지·hit-rate·요청/적중/미스/저장/엔트리/축출 카운트와 원문 미저장 안내를 표시한다 · 검증: 신규 spec 필요
- [x] `admin-ai-integrity-panel` DB 계량 상태(무결성) 패널 — 계량 원천·durable 여부·미계량 턴 수와 '감사 필요/누락 없음' 판정을 표시한다 · 검증: 기존(부분) e2e/readiness.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `admin-ai-note-no-source-pack-kb` [조사 노트] source pack sync·KB 관리 부재 — 이 페이지에는 source pack sync·KB 관리 UI가 없고 관련 코드는 PersonaStudio·Session에만 존재한다 · 검증: 기존 e2e/kb-source-packs.spec.ts
**의심 결함(에이전트 리포트)**:
- `admin-ai-initial-load`: 부분 실패 시 성공 데이터와 오류 배너가 공존해 어떤 패널이 실패했는지 구분하기 어렵다. engine-config 실패 시 엔진 패널은 '불러오는 중' 문구가 영구히 남는다(재시도 UI 없음).
- `admin-ai-refresh-button`: 엔진 설정 폼을 수정하고 저장하기 전에 새로고침을 누르면 편집 중이던 engine_url·model·mode가 서버 값으로 조용히 덮어써진다(변경사항 유실 경고 없음).
- `admin-ai-aria-busy`: aria-busy 외에 시각적 로딩 스피너/스켈레톤이 없어 데이터 미도착 시 지표가 '—'로만 보인다.
- `admin-ai-window-toggle`: 기간 변경 실패 시 이전 기간의 usage 데이터가 화면에 그대로 남은 채 오류 배너만 떠서 기간 라벨(예: '90일 DB 집계')과 실제 표시 데이터가 불일치할 수 있다.
- `admin-ai-daily-chart`: 막대별 상세가 title 속성에만 있어 키보드·스크린리더로는 개별 일자 값에 접근할 수 없다(전체 aria-label만 존재).
- `admin-ai-engine-provider-select`: **해결(2026-07-30)** — 옛 4종 라디오와 `messages_api` 별칭을 없애고 6종 provider select와 capability 상태를 단일 계약으로 연결했다.
- `admin-ai-engine-url-input`: **해결(2026-07-30)** — 서버가 제안된 URL의 capability를 조회한 뒤 연결 실패·모델/강도 불일치를 422로 거부하므로 잘못된 주소가 durable 설정을 덮지 않는다.
- `admin-ai-engine-save-button`: 저장 성공 후 health 재조회 실패는 catch(() => health)로 조용히 무시되며 이때 스테일 클로저의 이전 health를 쓴다. window.setTimeout(2400ms)을 언마운트 시 정리하지 않아 페이지 이탈 후 setState가 호출될 수 있다. 저장 확인(confirm) 없이 즉시 운영 엔진이 교체된다.
- `admin-ai-engine-loading-empty`: GET 실패 시에도 같은 '불러오는 중' 문구가 영구 표시되어 실패 상태를 로딩으로 오인하게 한다(패널 내 재시도 수단 없음).
## 9. 설정 (settings)
- [x] `settings-guard-require-auth` RequireAuth 가드 (미인증 → /login) — 로딩 중 BootScreen을 띄우고 미인증이면 state.from을 담아 /login으로 보낸다(역할 제한 없음) · 검증: 신규 spec 필요
- [x] `settings-guard-pending-approval` PendingApprovalGate (미승인 → /pending) — 미승인 사용자가 /settings에 접근하면 /pending으로 리다이렉트한다 · 검증: 신규 spec 필요
- [x] `settings-guard-onboarding` OnboardingGate (온보딩 미완료 → /onboarding) — onboardingCompletedAt이 null이면 /onboarding으로 보낸다(/settings는 관리자 예외 경로가 아님) · 검증: 신규 spec 필요
- [x] `settings-initial-load` 설정 데이터 일괄 로드 — 프로필·환경설정·지원 티켓·음성 프리셋을 병렬 조회하고 관리자면 엔진 설정·헬스·provider capability도 추가 조회한다 · 검증: e2e/settings.spec.ts
- [x] `settings-load-error-callout` 로드 실패 경고 콜아웃 — 로드 실패 시 화면 상단에 role=alert 경고 콜아웃으로 오류 메시지를 표시한다 · 검증: 신규 spec 필요
- [x] `settings-section-skeletons` 섹션별 로딩 스켈레톤 — 데이터 도착 전 role=status 스켈레톤을 띄우고 스크린리더용 텍스트를 숨김 제공한다 · 검증: 기존 e2e/settings.spec.ts
- [x] `settings-rail-nav-buttons` 좌측 레일 섹션 내비게이션 버튼 — 계정·지원·(관리자 AI 운영)·테마·알림·음성 버튼으로 해당 섹션에 스크롤하고 aria-current를 표시한다 · 검증: 신규 spec 필요
- [x] `settings-rail-nav-scrollspy` 스크롤 스파이 (IntersectionObserver) — 화면에 가장 많이 보이는 섹션을 감지해 레일 활성 항목을 자동 동기화한다 · 검증: 신규 spec 필요
- [x] `settings-rail-account-summary` 레일 계정 요약 카드·역할 pill — 이니셜 아바타·표시 이름·이메일·역할·소속 배지를 요약하고 관리자에게는 AI 헬스 pill을 더한다 · 검증: 기존(부분) e2e/settings.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `settings-account-display-name-input` 표시 이름 입력 — 입력 시 dirty ref를 켜서 이후 도착하는 서버 로드값이 입력을 덮어쓰지 않게 한다 · 검증: 기존 e2e/settings.spec.ts
- [x] `settings-account-email-readonly` 이메일 (읽기 전용) — 로그인으로 확인된 이메일을 disabled 입력으로 표시만 한다 · 검증: 기존(부분) e2e/settings.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `settings-account-affiliation-input` 소속 입력 — 강의·연구 운영에서 표시할 소속 문자열을 입력받는다 · 검증: 기존(부분) e2e/settings.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `settings-account-save-button` 계정 저장 버튼 — 표시 이름·소속을 PATCH 저장하고 응답으로 폼을 동기화한 뒤 '저장됨'을 2.4초 띄운다 · 검증: 기존 e2e/settings.spec.ts + e2e/db-persistence.spec.ts
- [x] `settings-support-ticket-list` 내 지원 요청 티켓 목록 — 내가 접수한 티켓을 상태·카테고리·우선순위 배지, 담당 그룹, 해결 메모와 함께 나열한다 · 검증: 기존 e2e/settings.spec.ts
- [x] `settings-support-error-state` 지원 요청 로드 실패 상태 — 티켓 조회만 실패하면 페이지를 깨뜨리지 않고 섹션 안에 role=alert 경고를 표시한다 · 검증: 신규 spec 필요
- [x] `settings-support-empty-state` 지원 요청 빈 상태 — 0건이면 '접수한 지원 요청이 없습니다', null이면 '저장소에 표시할 항목이 없습니다'를 표시한다 · 검증: 신규 spec 필요
- [x] `settings-engine-provider-select` AI provider 드롭다운 (관리자) — 6가지 provider 중 하나를 고르고 설치·인증·모델 조회 상태를 함께 표시한다 · 검증: e2e/settings.spec.ts
- [x] `settings-engine-url-input` AI 연결 주소 입력 (관리자) — URL을 로컬 상태에 반영하면서 이전 주소의 catalog를 무효화하고 새 주소 모델 조회 전 저장을 막는다 · 검증: e2e/settings.spec.ts + admin.spec.ts의 동일 계약
- [x] `settings-engine-model-select` 기본 모델 드롭다운 (관리자) — provider capability에 있는 모델만 선택하고 최근 변경자·저장 상태 메타를 함께 표시한다 · 검증: e2e/settings.spec.ts
- [x] `settings-engine-effort-select` 추론 강도 드롭다운 (관리자) — 선택 모델이 지원하는 강도만 고를 수 있다 · 검증: e2e/settings.spec.ts
- [x] `settings-engine-save-button` 운영 설정 저장 버튼 (관리자) — provider·주소·모델·추론 강도를 PATCH 저장한 뒤 헬스를 재조회해 상태 칩을 갱신한다 · 검증: e2e/settings.spec.ts + e2e/db-persistence.spec.ts
- [x] `settings-engine-health-chip` 응답 생성 헬스 상태 칩 (관리자) — engine 서비스 상태를 정상/제한 운영/중단/확인 중 텍스트와 detail·metric으로 표시한다 · 검증: 기존(부분) e2e/settings.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `settings-theme-dark-toggle` 다크 모드 토글 — role=switch 토글로 즉시 data-theme을 바꾸고 localStorage에 저장해 전역 구독자와 동기화한다 · 검증: 신규 spec 필요
- [x] `settings-theme-save-button` 테마 저장 버튼 — 현재 다크 여부를 theme으로 서버 환경설정에 PATCH 저장하고 '저장됨'을 표시한다 · 검증: 신규 spec 필요
- [x] `settings-voice-preset-radiogroup` 음성 프리셋 선택 라디오 카드 — 서버 프리셋 목록을 role=radio 카드로 나열하고 클릭 시 voice_preset_id를 로컬 상태에 반영한다 · 검증: 기존 e2e/voice.spec.ts
- [x] `settings-voice-rate-slider` 말하기 속도 슬라이더 — 0.8x~1.2x range 입력으로 voice_rate를 조절하고 현재값을 실시간 표시한다 · 검증: 기존(부분) e2e/settings.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `settings-voice-save-button` 음성 저장 버튼 — 프리셋 ID와 말하기 속도를 서버 환경설정에 PATCH 저장하고 '저장됨'을 표시한다 · 검증: 기존 e2e/settings.spec.ts
- [x] `settings-voice-empty-state` 음성 프리셋 빈 상태 — 등록 프리셋이 없으면 안내를 표시하고 슬라이더·저장 버튼을 숨긴다 · 검증: 신규 spec 필요
- [x] `settings-notify-toggles` 알림 항목 토글 (역할 필터) — 공통 3종에 더해 담당 학습자 진행은 teacher/admin, 가입 승인은 admin에게만 role=switch로 표시한다 · 검증: 기존 e2e/settings.spec.ts
- [x] `settings-notify-save-button` 알림 저장 버튼 — 토글 상태를 theme·voice 값과 함께 환경설정에 PATCH 저장하고 '저장됨'을 표시한다 · 검증: 기존 e2e/settings.spec.ts + e2e/db-persistence.spec.ts
- [x] `settings-saved-flash-indicator` '저장됨' 플래시 피드백 — 각 섹션 저장 성공 시 체크 아이콘과 문구를 2.4초 표시했다 자동으로 지운다 · 검증: 신규 spec 필요
- [x] `settings-topbar-theme-toggle` 톱바 테마 전환 버튼 (공용 셸) — 상단 Topbar의 해/달 버튼이 설정 섹션의 다크 모드 토글과 같은 전역 스토어를 전환한다 · 검증: 신규 spec 필요
- [x] `settings-topbar-logout` 로그아웃 버튼 (공용 Topbar) — 설정 화면의 로그아웃은 공용 Topbar 아이콘에 있으며 서버 로그아웃 후 /login으로 이동한다 · 검증: 신규 spec 필요
- [x] `settings-topbar-role-switch` 역할 공간 전환 링크 (공용 Topbar) — 복수 역할 권한자에게 학습자/교수자/관리자 홈 이동 링크를 표시한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
- [x] `settings-avatar-upload-missing` 아바타 업로드 UI (부재) — 브리핑상 설정에 있어야 할 아바타 업로드가 없고 업로드 API는 온보딩에서만 쓰인다 · 검증: 신규 spec 필요 · **구현 완료(2026-07-27 소유자 결정: 설정에 추가)** — 신규 spec GREEN(full-sweep-settings 4/4 desktop+mobile)
- [x] `settings-consent-management-missing` 동의 관리·철회 UI (부재) — 브리핑상 설정에 있어야 할 동의 관리·철회가 없고 withdrawConsent는 어떤 UI에서도 호출되지 않는다 · 검증: 신규 spec 필요 · **구현 완료(2026-07-27 소유자 결정: 전체 흐름)** — 철회·재동의 실 API round-trip GREEN(full-sweep-settings 44/44 무회귀)
**의심 결함(에이전트 리포트)**:
- `settings-initial-load`: supportTickets만 개별 catch되고 나머지는 하나라도 실패하면 페이지 전체 오류로 전환됨(부분 성공 데이터 폐기). 서버 theme 값이 로드 시 setDark를 호출해 사용자가 Topbar에서 방금 바꾼 로컬 테마를 되돌릴 수 있음.
- `settings-load-error-callout`: 재시도 버튼이 없어 사용자는 새로고침 외 복구 수단이 없음.
- `settings-rail-account-summary`: 서버 profile의 avatar_url을 무시하고 항상 이니셜만 렌더 — Topbar는 업로드된 아바타 이미지를 보여줘 화면 간 불일치.
- `settings-account-save-button`: saveProfile에 try/catch가 없어 실패 시 unhandled rejection — 사용자에게 오류 표시가 전혀 없고 저장 실패를 알 수 없음. 저장 중 busy 상태도 없어 연타 가능.
- `settings-support-ticket-list`: 읽기 전용 목록만 있고 이 화면에서 새 지원 요청을 접수하는 UI가 없음.
- `settings-support-empty-state`: 빈 상태가 로딩용 SettingsLoadingState(점멸 dot, role=status) 컴포넌트를 재사용해 영구 로딩 중처럼 오인될 수 있음. 두 빈 상태가 같은 testId(settings-support-empty)를 공유.
- `settings-engine-provider-select`: **해결(2026-07-30)** — 옛 DOM 조회 라디오를 제어 select 상태로 교체하고 6종 provider capability와 연결했다.
- `settings-engine-url-input`: **해결(2026-07-30)** — 저장 전 서버 capability 검증으로 잘못된 URL을 durable 설정에 반영하지 않는다.
- `settings-engine-save-button`: **해결(2026-07-30)** — React 상태에서 provider·모델·강도를 직접 전송하고 저장 실패를 화면 오류로 표면화한다.
- `settings-engine-health-chip`: 헬스 조회 실패를 조용히 삼키고 null 처리 — 실패해도 '확인 중'으로만 남아 장애와 로딩을 구분할 수 없음. 기본 status fallback이 'degraded'라 로딩 중에도 경고색 칩으로 렌더됨.
- `settings-theme-save-button`: savePreferences에 try/catch 없음(실패 무통보). 저장 안 하면 서버 theme이 남아 다음 /settings 방문 로드 시 로컬 토글값을 되돌림 — 토글 즉시 반영과 서버 저장이 이중 상태.
- `settings-voice-preset-radiogroup`: 카드 우측 SpeakerIcon(재생 삼각형)이 role=presentation·aria-hidden 장식일 뿐 미리듣기 기능이 없음 — 재생 버튼처럼 보이는 죽은 어포던스. 화살표 키 라디오 이동도 없음.
- `settings-voice-save-button`: try/catch 없음(실패 무통보). savePreferences가 theme·notifications까지 통째로 전송해 다른 섹션의 미저장 변경도 함께 확정됨.
- `settings-notify-save-button`: try/catch 없음(실패 무통보). 알림 알림 자체는 저장만 되고 실제 알림 전달 채널이 이 화면에서 확인 불가.
- `settings-saved-flash-indicator`: role=status/aria-live가 없어 스크린리더는 저장 완료를 인지하지 못함.
- `settings-topbar-logout`: auth.tsx의 logout이 API 실패를 무시하고 로컬 세션만 지우는 구조인지 확인 필요 — Settings 본문에는 로그아웃·계정 삭제 등 세션 관리 UI가 전무.
- `settings-avatar-upload-missing`: 온보딩 이후 아바타를 바꿀 UI가 앱 어디에도 없음.
- `settings-consent-management-missing`: 동의 철회 경로가 UI에 전혀 노출되지 않아 규정(동의 철회권) 관점에서 공백 — dead code 상태의 withdrawConsent.
## 10. 공통 셸·GNB·라우팅 가드 (shell)
- [x] `shell-topbar-brand-link` 톱바 브랜드 로고 링크 — 워드마크 클릭 시 로그인 상태면 루트로, 미로그인이면 /login으로 이동한다 · 검증: 신규 spec 필요
- [x] `shell-topbar-role-context-label` 역할 컨텍스트 라벨 — 톱바에 현재 역할 라벨 또는 페이지가 넘긴 contextLabel을 표시한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `shell-topbar-space-switcher` 공간 전환 링크 그룹 — 권한에 따라 학습자/교수자/관리자 공간 링크를 노출하고 현재 경로 접두 일치 시 활성 표시한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `shell-topbar-theme-toggle` 다크/라이트 테마 토글 — 아이콘 버튼으로 data-theme을 전환하고 localStorage에 저장하며 Settings와 같은 스토어를 공유한다 · 검증: 기존(부분) e2e/auth-visual.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `shell-topbar-user-chip` 사용자 아바타·이름 표시 — avatarUrl 이미지(없으면 이니셜)와 이름을 톱바 우측에 표시한다(클릭 동작 없음) · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `shell-topbar-logout` 로그아웃 버튼 — 로그아웃 API 호출 후(실패해도) 로컬 사용자 상태를 비우고 /login으로 replace 이동한다 · 검증: 신규 spec 필요
- [x] `shell-sidebar-learner-nav` 학습자 사이드바 메뉴 — 대시보드·학습·기록·설정 4개 NavLink를 표시하고 활성 항목을 알약으로 강조한다 · 검증: 기존(부분) e2e/layout-visual-gate.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `shell-sidebar-teacher-nav` 교수자 사이드바 메뉴 — 콘솔·학생 분석·페르소나·설정 4개 NavLink를 표시한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `shell-sidebar-admin-nav` 관리자 사이드바 메뉴 — 관리자 5 + 교수자 3 + 학습자 3 + 설정 1, 총 12개 NavLink를 한 목록으로 표시한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `shell-sidebar-ethics-footnote` 사이드바 윤리 고지 — 교육용 시뮬레이션이며 실제 치료·진단을 대체하지 않는다는 고지를 항상 표시한다 · 검증: 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `shell-appshell-focus-modes` 셸 레이아웃 변형(hideNav/hideTopbar/bleed/wide) — 페이지 props로 사이드바 숨김·전체화면·풀블리드·넓은 작업폭을 제공하고 미인증 시 사이드바를 자동 숨긴다 · 검증: 기존 e2e/session-layout.spec.ts
- [x] `shell-appshell-role-theming` body[data-role] 역할 테마 적용 — navRole에 따라 body에 data-role을 설정해 역할별 팔레트를 적용하고 언마운트 시 복원한다 · 검증: 신규 spec 필요
- [x] `shell-auth-bootstrap-loading` 인증 부트스트랩 로딩 화면 — 앱 시작 시 세션을 복원하고 완료 전까지 모든 가드가 중립 BootScreen을 표시한다 · 검증: 신규 spec 필요
- [x] `shell-suspense-fallback` lazy 라우트 청크 로딩 fallback — 모든 페이지가 lazy import이므로 청크 로딩 동안 Suspense fallback으로 BootScreen을 표시한다. 배포 후 stale 청크 실패는 문서 재로드 1회로 최신 배포를 받고 연속 실패 시 ErrorBoundary로 넘긴다 · 검증: `e2e/full-sweep-shell.spec.ts` + `e2e/chunk-recovery-preview.spec.ts`, 공개 강제 실패 포함 GREEN(2026-07-28)
- [x] `shell-guard-require-auth` RequireAuth 미인증 리다이렉트 — 보호 라우트에서 미인증이면 원래 경로를 state.from에 담아 /login으로 replace 리다이렉트한다 · 검증: 기존 e2e/learner.spec.ts
- [x] `shell-guard-role-restriction` RequireAuth 역할 제한 리다이렉트 — 역할 불일치 시 빈 화면 대신 자기 역할 홈으로 보내며 admin·adminAccess 예외를 적용한다 · 검증: 기존 e2e/admin.spec.ts + e2e/teacher.spec.ts
- [x] `shell-guard-pending-approval` PendingApprovalGate 승인 대기 리다이렉트 — 미승인 사용자는 어떤 경로든 /pending으로 보내고 승인 사용자가 /pending에 오면 초기 경로로 되돌린다 · 검증: 신규 spec 필요
- [x] `shell-guard-onboarding` OnboardingGate 온보딩 리다이렉트 — 온보딩 미완료자는 /onboarding으로(admin의 /admin*만 예외), 완료자가 /login·/onboarding에 오면 initialPathForUser로 되돌린다 · 검증: 기존(부분) e2e/auth.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실순회)
- [x] `shell-root-redirect` 루트 경로 역할별 분기 — / 접근 시 미승인은 /pending, 인증 사용자는 initialPathForUser, 미인증은 /login으로 replace 이동한다 · 검증: 기존 e2e/admin.spec.ts
- [x] `shell-unknown-route-redirect` 미정의 경로 리다이렉트 — 정의되지 않은 모든 경로는 404 화면 없이 /로 replace되어 역할 홈으로 재분기된다 · 검증: 신규 spec 필요
- [x] `shell-route-error-boundary` 라우트 렌더 오류 fallback — 렌더 예외 시 role=alert 진단 화면을 띄우고 경로가 바뀌면 오류 상태를 자동 리셋한다. 최신 버전 재로드·홈 이동 복구 액션을 제공한다 · 검증: 기존(부분) e2e/admin.spec.ts + 탐색 순회 + chunk recovery focused GREEN(2026-07-28)
- [x] `shell-scroll-reset-on-navigate` 경로 변경 시 스크롤 최상단 리셋 — scrollRestoration을 manual로 두고 경로 변경·bfcache 복원 시 스크롤을 최상단으로 되돌린다 · 검증: 기존 e2e/admin.spec.ts
- [x] `shell-auth-expired-listener` 세션 만료 이벤트 자동 로그아웃 — API 계층이 401에서 발행하는 auth-expired 이벤트를 받아 사용자 상태를 비워 /login으로 유도한다 · 검증: 기존 e2e/learner.spec.ts
- [x] `shell-avatar-expression-board` Live2D 페르소나 표정 보드 — P4~P7 모델별 공유 표정 라이브러리 전체를 모델 경로·모션·페이드 메타와 함께 정적 렌더하는 QA 보드다 · 검증: 기존 e2e/avatar-expression-lab.spec.ts
- [x] `shell-avatar-preview-dev-page` 서연 SVG 아바타 dev 미리보기 — 인증 없이 접근 가능한 dev 페이지로 4개 상태 애니메이션과 대표 표정 12종 그리드를 표시한다 · 검증: 신규 spec 필요
**의심 결함(에이전트 리포트)**:
- `shell-topbar-space-switcher`: 일반 학습자·교수자에게는 아예 미노출(의도). 컨테이너가 nav가 아닌 div에 aria-label만 부여됐고, accessibleRolesFor 결과를 canAccessRole로 재필터링하는 중복 로직이 있음.
- `shell-sidebar-admin-nav`: 12개 항목이 역할 그룹 구분(구분선·소제목) 없이 한 열에 나열되고 review 아이콘이 5개 항목에 중복 사용되어 스캔성이 낮음.
- `shell-auth-bootstrap-loading`: me 호출 실패 사유(네트워크 오류 vs 미인증)를 구분하지 않고 catch에서 조용히 user=null 처리해 오프라인 시 로그인 화면으로 떨어짐.
- `shell-suspense-fallback`: ~~청크 로드 실패(배포 후 stale 해시)는 Suspense가 아닌 ErrorBoundary로 전파되는데 별도 새로고침 유도 UI 없이 진단 표만 노출됨.~~ **FIXED(2026-07-28)**`vite:preloadError`에서 경로별 자동 새로고침을 1회만 수행하고 정상 라우트 렌더 뒤 잠금을 해제한다. 개발/production preview/공개 도메인 강제 청크 실패 E2E GREEN.
- `shell-guard-onboarding`: 온보딩 미완료 admin은 /admin*만 예외라 /settings·/teach 접근 시 /onboarding으로 튕김. 미인증 방문자의 /dev/avatar-preview는 통과되지만 로그인된 미승인·미온보딩 사용자는 이 dev 페이지도 게이트에 걸려 접근 불가.
- `shell-root-redirect`: onboardingCompletedAt==null 분기와 최종 분기가 동일하게 initialPathForUser를 호출해 사실상 죽은 중복 분기.
- `shell-unknown-route-redirect`: 전용 404 페이지가 없어 오타 URL이 조용히 홈으로 이동, 사용자가 링크 오류를 인지하지 못함.
- `shell-route-error-boundary`: 진단의 componentStack은 운영 식별 증거로 유지한다. ~~재시도/홈 버튼이 없어 URL을 직접 바꿔야 함.~~ **FIXED(2026-07-28)**`최신 버전 다시 불러오기``홈으로 이동` 액션을 추가했다.
- `shell-avatar-expression-board`: 학습자 사이드바 등 어떤 GNB에도 링크가 없어 URL 직접 입력으로만 접근 가능한 숨은 라우트. 콘텐츠가 QA용인데 learner 라우트(/learn/*)에 위치.
- `shell-avatar-preview-dev-page`: 인증 가드 없는 dev 라우트가 프로덕션 라우터에 그대로 포함되어 누구나 페르소나 리그를 열람 가능(코드 주석상 의도지만 배포 노출 여부 재검토 필요).
## 발견 결함 로그 (RED → GREEN 추적)
| # | 영역 | 증상 | 재현 spec | 상태 |
|---|---|---|---|---|
| 1 | auth | 온보딩 연락처에 임의 텍스트("전화번호아님텍스트") 입력 시 클라이언트·서버 모두 통과되어 DB에 저장됨 | 서버: `test_user_onboarding_validation.py`(#13) · 클라: `Onboarding.tsx` `isValidPhone` + 구체 오류 문구 | **FIXED** (typecheck GREEN, e2e 재검증은 최종 게이트에서) |
| 2 | learner-home | 신규 학습자 첫 추천이 고난도 P1인데 AI 코치 문구는 "낮은 난도부터"를 권함 — 상호 모순 | 소유자 결정(기초 우선) → `starterPersona()` 난도 오름차순 기본 선택, RED→GREEN(full-sweep-learner-home 신규 2건) | **FIXED** (42/42 무회귀) |
| 3 | learner-home | 대시보드 탭 전환 후에도 패널 aria-label이 "최근 기록 패널"로 고정 | `LearnerHome.tsx` — dashTab에 따라 "성장 지표 패널"로 전환 | **FIXED** (typecheck GREEN) |
| 4 | professor | 학생 분석 테이블이 347명 전행 렌더(페이지네이션·가상화 없음) — 규모 성능·탐색성 우려 | 소유자 결정(지금 가상화) → @tanstack/react-virtual 세로 가상화(동적 행 높이·펼침 재측정), RED(300행 전량 렌더 관측)→GREEN | **FIXED** (40/40 무회귀 + professor 게이트 4/4) |
| 5 | admin-ai | `/admin/ai` 정상 렌더에도 부트 watchdog 진단 오버레이 표시(`asset=module-not-discovered` 오탐) | `admin.spec.ts` "keeps the boot diagnostic overlay off a rendered AI operations page" — RED 재현 후 `index.html` 셀렉터에 `.aic` 추가 | **FIXED** (1 passed 12.2s) |
| 6 | auth/admin | 관리자가 보류(pending) 처리한 사용자가 dev-login 재로그인 시 자동 재승인됨 — dev 한정 | 소유자 결정: **현행 유지**(dev/E2E 편의 의도 확정, 프로덕션은 dev-login 자체 폐쇄) | **CLOSED** (의도 확정, 코드 변경 없음) |
| 7 | auth | /pending "승인 상태 새로고침"이 여전히 미승인일 때 아무 피드백 없음(확인 수행 여부 불명) | `PendingApproval.tsx` — 미승인/실패 시각 포함 안내 문구 + refresh 아이콘 신설 | **FIXED** (typecheck GREEN) |
| 8 | 백엔드 마스킹 | 평가/코칭 문장의 일반 단어(유의점은·방치되는·마무리했을·연결감과·표현함을·어지러움은)가 `[NAME]`으로 오탐 치환되어 문장 훼손 | `test_orchestrator_masking.py::test_mask_pii_does_not_mask_evaluator_abstract_words_as_names` — RED 재현 후 `guardrail.py` 꼬리 글자 가드 추가 | **FIXED** (마스킹 18/18, 의존 스위트 69/69, 백엔드 전체 401/401) |
| 9 | settings | 온보딩 입력 항목 및 동의 철회를 설정 화면에서 수정할 수단 없음 | 소유자 결정 → 아바타 변경(#15)·동의 철회/재동의(#16) 구현 완료. 닉네임·자기소개·연락처·주소 편집은 후속 확장 후보로 남김 | **부분 FIXED** (#15·#16 해결) |
| 10 | session | 회기 종료 확인 다이얼로그만 반말 문체("종료할까?", "이동한다") — 앱 전반 존댓말과 불일치 | `Session.tsx` — "종료할까요?"/"이동합니다."로 통일 | **FIXED** (typecheck GREEN) |
| 11 | 백엔드 리드모델 | 리뷰 요약 "하늘**와**의 회기는" — 받침 있는 이름 뒤 조사 오류 | `test_review_summary_josa.py` — RED 재현 후 `_josa_wa_gwa` 헬퍼 추가 | **FIXED** (3/3, 백엔드 409/409) |
| 12 | 백엔드 리드모델 | 리뷰 헤더 난도가 "P4 · easy" 원문 노출(타 화면은 "기초") | `test_review_summary_josa.py::ReviewPersonaLabelTest``_persona_label` 한글 매핑 추가 | **FIXED** (백엔드 410/410) |
| 13 | auth | 온보딩 서버 검증 부재(#1의 서버측) — `OnboardingRequest`/`UserProfilePatch` phone 자유 문자열 허용 | `test_user_onboarding_validation.py` — RED 3건 재현 후 `_validate_phone_format` 추가 | **FIXED** (5/5, 백엔드 406/406 당시) · 클라이언트 측 안내는 #1에서 계속 |
| 14 | persona-studio | 저작 모드 좌측 레일(.ps-rail)이 무조건 `display:none` — 숨겨진 인터랙티브 마크업 DOM 잔존 | 소유자 결정(정리) → 레일 JSX·전용 CSS 삭제, 특성화 테스트 toHaveCount(0) RED→GREEN | **FIXED** (20/20 무회귀 + persona 게이트 2/2 + dead-code 통과) |
| 15 | settings | 설정에 아바타 변경 UI 부재(업로드 API는 온보딩 전용) — #9 세분화 | 소유자 결정(설정에 추가) → 계정 섹션 업로드 컨트롤+미리보기+PATCH avatar_url, fixme 해제 RED→GREEN | **FIXED** (4/4 desktop+mobile, 44/44 무회귀) |
| 16 | settings/auth | 동의 철회 UI 부재 + `withdrawConsent` dead code — #9 세분화 | 소유자 결정(전체 흐름) → 설정 동의 섹션(상태·버전 표시+철회 2단계 확인+재동의), 세션 프리스타트 기존 재동의 게이트와 연결, fixme 해제 RED→GREEN | **FIXED** (철회·재동의 실 API round-trip GREEN) |
| 17 | settings | 스크롤 스파이가 마지막 섹션(음성)을 활성화하지 못함 — 0 임계값 부재 + 비율 우세 휴리스틱이 scroll-margin 정렬 지점과 충돌(모바일에서 두드러짐) | `full-sweep-settings.spec.ts` 모바일 2건 RED 재현 → `Settings.tsx` 밴드 최하단 섹션 규칙 + 스무스 스크롤 억제 + 바닥 폴백 | **FIXED** (settings desktop+mobile 26/26) |
| 18 | session | 모바일 겹침 2건: (a) 절대배치 코치 넛지가 음성 건너뛰기 클릭 가로챔, (b) 타임바 등장 시 3행 그리드 템플릿 밀림으로 컨텍스트 패널이 타임바 종료 버튼을 덮음 | `full-sweep-session.spec.ts` 모바일 RED 재현 → `session.css` `:has(> .sx-timebar)` 4행 템플릿 + 모바일 넛지 플로우 배치 | **FIXED** (session desktop+mobile 28/28, session-layout 8/8, layout-visual-gate 15/15) |
| 19 | admin | **관리자 콘솔 무한 렌더 루프 프리즈** — users API 실패(usersData=null) 상태 탭 클릭·실DB 스케일 티켓 검색 입력 시 TanStack `_autoResetPageIndex`↔setState 동기 루프로 영구 프리즈 | 에이전트 CDP 채증 + 결정적 재현 스펙 2건 → `Admin.tsx` users/services 메모화 + `autoResetPageIndex:false` | **FIXED** (재현 2건 GREEN 포함 admin 19/19) |
| 20 | admin/백엔드 | **복수 코호트 조용한 유실** — POST/PATCH `/admin/users`가 cohort_ids 첫 항목만 저장(`_cohort_value`가 첫 값만 취함) | 의도적 RED e2e + `test_admin_ops.py::CohortSerializationTest` → 콤마 직렬화 + `notifications.py` 배열 겹침 비교 | **FIXED** (백엔드 412/412, e2e 재실행 GREEN) |
| 21 | session-review/백엔드 | 감정 밸런스 타임라인 데이터 소스가 항상 빈 배열 — 사문 렌더 경로 | 소유자 결정(지금 구현) → 혼합 접근: deep 스키마 `turn_valence` 신설(LLM 산출)+저장 데이터 결정론 파생 폴백(counselorBaseline=rapport_signal 누적 평균), `test_review_valence.py` RED 8건→GREEN 9/9 | **FIXED** (백엔드 421/421, e2e 차트 렌더 검증 포함 14/14) |
| 22 | persona-studio/백엔드 | **공개 페르소나 보관 전면 불능** — DELETE `/personas/{id}`가 실DB에서 항상 503. `archive_persona_family`의 UPDATE…FROM RETURNING에서 무한정 `code`가 target.code와 모호(AmbiguousColumnError) | 실API RED 재현(QAX1 초안 생성→DELETE 503) → RETURNING 컬럼 card. 한정 → 재시작 후 같은 호출 200/archived | **FIXED** (백엔드 412/412 + 실API GREEN) |
| 23 | 스모크 스크립트 | `smoke-resistance-openness-db.py`의 advice_jump 단언(turn_client_state ≥5)이 엔진 변동성에 민감 — 조언점프 5턴 중 2턴에서 evaluator가 client_state 라벨을 비워 3행만 저장(2026-07-27 실행, 코드 회귀 아님 — 당일 diff는 deep-loop 한정) | 스모크 실행 로그 | OPEN·스모크 단언 완화 검토(예: ≥3 또는 재시도) |