feat: 운영 안정성과 세션 음성 경험 개선
This commit is contained in:
parent
facc4ad2d9
commit
c788343467
95 changed files with 8431 additions and 1785 deletions
|
|
@ -35,14 +35,14 @@
|
|||
상세: `docs/ops/tailscale-vnet-runtime-2026-06-27.md`.
|
||||
- [ ] **실배포 `infra/.env` owner-secret fill-in + real preflight** — 필수 소유자 비밀값: `APP_DB_PASSWORD`,
|
||||
`OAUTH_GOOGLE_CLIENT_ID`, `OAUTH_GOOGLE_CLIENT_SECRET`, `OPENAI_API_KEY`, `SESSION_SECRET`, production-safe
|
||||
engine/voice flags. 로컬 stray 값(`ENGINE_MODE=claude_p`, prod sample TTS flag 등)은 운영값으로 복사 금지.
|
||||
engine/voice flags. 로컬 stray 값(`ENGINE_MODE=claude_cli`, prod sample TTS flag 등)은 운영값으로 복사 금지.
|
||||
실제 secret 주입 후 `scripts/check-deploy-preflight.py` DB 포함 모드 + 배포지 health를 별도 증거로 닫는다.
|
||||
- [ ] **공개 Google OAuth 실제 `/turn` proof** — 로그인 가능한 계정으로 `storageState` 캡처 후 `E2E_PUBLIC_AUTH=1`
|
||||
+ `chromium-public-auth` 1회 통과 필요. (소유자 지시로 보류 중.) 실행 명령은 대시보드 "다음 실행 명령" 참조.
|
||||
- [ ] **음성 캐스케이드 live** — 로컬 API 실제 OpenAI STT/TTS 경로는 live WS smoke 통과. 남은 범위: 실제 Deepgram
|
||||
interim/final WSS, 물리 마이크, 공개 WSS, 50분 양방향 장시간 실측.
|
||||
- [ ] **claude_cli ↔ Messages API 폴백 동일성** — `ANTHROPIC_API_KEY`가 있어야 Messages API 경로 동일성 검증 가능.
|
||||
claude_cli 경로는 게이트웨이 probe로 live 실측 완료.
|
||||
- [ ] **음성 캐스케이드 live** — 로컬 API 실제 OpenAI STT/TTS와 P1 Higgs 실제 문장 WAV 합성 경로는 live smoke 통과.
|
||||
Higgs는 무참조 synthetic seed·dev-only로 유지한다. 남은 범위: 실제 Deepgram interim/final WSS, 물리 마이크,
|
||||
공개 WSS, 50분 양방향 장시간 실측과 상업 라이선스가 명확한 운영 TTS provider 통합.
|
||||
- [ ] **claude_cli ↔ Anthropic API live 동일성** — provider 라우팅·Anthropic `/v1/models` 탐색·지원 추론 강도·관리자 fail-closed 저장 경로는 구현 완료. 남은 범위는 연구팀/기관 `ANTHROPIC_API_KEY`를 게이트웨이 호스트에 주입한 live 응답·계량·오류 표면화 비교다. Claude CLI·Codex CLI(Terra/Medium)·Agy CLI(Gemini 3.6 Flash/High)는 로컬 live probe를 통과했다.
|
||||
- [ ] **재부팅 후 watchdog smoke** — `watch-public-runtime.ps1` + Scheduled Task가 재부팅 후 엔진/API/터널을 복구하고
|
||||
public `/turn`이 통과하는지 실측. 재부팅 불가로 미실행(parser/check-only 경로는 확인). DNS 개통 후
|
||||
`api-vnet.18ka.net`은 `-AdditionalPublicHealthUrls`로 명시 추가. 상세: `docs/ops/public-runtime-watchdog.md`.
|
||||
|
|
|
|||
|
|
@ -529,8 +529,8 @@
|
|||
- [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를 allSettled로 호출해 성공분만 반영하고 실패 메시지는 중복 제거해 배너에 잇는다 · 검증: 기존 e2e/admin.spec.ts
|
||||
- [x] `admin-ai-refresh-button` 새로고침 버튼 — 세 가지 데이터를 모두 다시 불러오며 로딩 중 비활성화된다 · 검증: 신규 spec 필요
|
||||
- [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 필요
|
||||
|
|
@ -543,11 +543,12 @@
|
|||
- [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-mode-radiogroup` AI 운영 방식 라디오그룹(4종) — claude_cli·claude_api·openai·solar를 role=radio 버튼으로 제공하며 저장 버튼을 눌러야 서버에 반영된다 · 검증: 기존 e2e/admin.spec.ts + e2e/db-persistence.spec.ts
|
||||
- [x] `admin-ai-engine-url-input` 연결 주소 입력 — engine_url을 제어 컴포넌트로 편집하며 게이트웨이·API base URL 힌트를 제공한다 · 검증: 기존 e2e/admin.spec.ts
|
||||
- [x] `admin-ai-engine-model-input` 기본 모델 입력 — 기본 model 문자열을 편집하며 평가 모델 override가 없을 때 적용된다고 안내한다 · 검증: 기존 e2e/db-persistence.spec.ts
|
||||
- [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` 운영 설정 저장 버튼 — 모드·URL·모델을 PATCH 저장하고 health를 재조회하며 2.4초간 '저장됨' 배지를 띄운다 · 검증: 기존 e2e/admin.spec.ts + e2e/db-persistence.spec.ts
|
||||
- [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 실순회)
|
||||
|
|
@ -560,8 +561,8 @@
|
|||
- `admin-ai-aria-busy`: aria-busy 외에 시각적 로딩 스피너/스켈레톤이 없어 데이터 미도착 시 지표가 '—'로만 보인다.
|
||||
- `admin-ai-window-toggle`: 기간 변경 실패 시 이전 기간의 usage 데이터가 화면에 그대로 남은 채 오류 배너만 떠서 기간 라벨(예: '90일 DB 집계')과 실제 표시 데이터가 불일치할 수 있다.
|
||||
- `admin-ai-daily-chart`: 막대별 상세가 title 속성에만 있어 키보드·스크린리더로는 개별 일자 값에 접근할 수 없다(전체 aria-label만 존재).
|
||||
- `admin-ai-engine-mode-radiogroup`: ENGINE_MODE_LABEL에는 messages_api 라벨이 있지만 선택지(ENGINE_MODES)에는 없어, 서버가 engine_mode='messages_api'를 반환하면 어떤 라디오도 선택 표시되지 않는다. 또 role=radio이지만 방향키 이동 등 라디오 키보드 패턴이 구현되어 있지 않다.
|
||||
- `admin-ai-engine-url-input`: URL 형식 검증이 없어(공백만 아니면 저장 가능) 잘못된 주소도 그대로 PATCH된다.
|
||||
- `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 실패 시에도 같은 '불러오는 중' 문구가 영구 표시되어 실패 상태를 로딩으로 오인하게 한다(패널 내 재시도 수단 없음).
|
||||
|
||||
|
|
@ -570,7 +571,7 @@
|
|||
- [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` 설정 데이터 일괄 로드 — 프로필·환경설정·지원 티켓·음성 프리셋을 병렬 조회하고 관리자면 엔진 설정·헬스도 추가 조회한다 · 검증: 기존 e2e/settings.spec.ts
|
||||
- [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 필요
|
||||
|
|
@ -583,10 +584,11 @@
|
|||
- [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-mode-radiogroup` AI 운영 방식 세그먼트 라디오 (관리자) — 4가지 연결 방식 버튼 중 하나를 골라 로컬 상태에 반영한다 · 검증: 기존 e2e/settings.spec.ts
|
||||
- [x] `settings-engine-url-input` AI 연결 주소 입력 (관리자) — 응답 생성 서비스 연결 URL을 로컬 상태와 ref에 반영한다 · 검증: 기존(부분) e2e/settings.spec.ts + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
|
||||
- [x] `settings-engine-model-input` 기본 모델 입력 (관리자) — 기본 모델 이름을 입력받고 최근 변경자·저장 상태 메타를 함께 표시한다 · 검증: 기존 e2e/settings.spec.ts
|
||||
- [x] `settings-engine-save-button` 운영 설정 저장 버튼 (관리자) — 운영 방식·주소·모델을 PATCH 저장한 뒤 헬스를 재조회해 상태 칩을 갱신한다 · 검증: 기존 e2e/settings.spec.ts + e2e/db-persistence.spec.ts
|
||||
- [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 필요
|
||||
|
|
@ -611,9 +613,9 @@
|
|||
- `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-mode-radiogroup`: role=radio 버튼 그룹에 화살표 키 이동(roving tabindex)이 없어 키보드 접근성 미흡. 라벨 맵에는 messages_api가 있으나 선택지에는 없어 서버가 그 값을 반환하면 아무 라디오도 체크되지 않음.
|
||||
- `settings-engine-url-input`: URL 형식 검증이 전혀 없음(공백만 아니면 저장 가능).
|
||||
- `settings-engine-save-button`: 선택 모드를 React 상태 대신 document.querySelector('[data-engine-mode][aria-checked=true]') DOM 조회로 읽는 안티패턴 — 마크업 변경에 취약. PATCH 실패 시 try/catch 없음(unhandled rejection, 사용자 피드백 없음).
|
||||
- `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 장식일 뿐 미리듣기 기능이 없음 — 재생 버튼처럼 보이는 죽은 어포던스. 화살표 키 라디오 이동도 없음.
|
||||
|
|
@ -639,14 +641,14 @@
|
|||
- [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을 표시한다 · 검증: 신규 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 + 탐색 순회 · 탐색 GREEN(2026-07-27 실브라우저 순회)
|
||||
- [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
|
||||
|
|
@ -657,11 +659,11 @@
|
|||
- `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 없이 진단 표만 노출됨.
|
||||
- `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을 직접 바꿔야 복구됨.
|
||||
- `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 라우트가 프로덕션 라우터에 그대로 포함되어 누구나 페르소나 리그를 열람 가능(코드 주석상 의도지만 배포 노출 여부 재검토 필요).
|
||||
|
||||
|
|
|
|||
|
|
@ -60,8 +60,19 @@ Use this when you want to force a runtime restore immediately:
|
|||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\start-public-runtime.ps1
|
||||
```
|
||||
|
||||
`start-public-runtime.ps1` now verifies or starts the engine gateway before it
|
||||
starts the prod API and cloudflared.
|
||||
`start-public-runtime.ps1`는 관리자·인증 제어면과 엔진을 분리한다. 이미
|
||||
`environment=prod`, `db=true`인 API와 정상 웹·터널은 유지하고, 엔진만 실패한 경우
|
||||
엔진만 복구한다. 엔진이 늦게 준비돼도 관리자·인증 API 기동을 막지 않는다.
|
||||
|
||||
API 코드 변경을 운영 프로세스에 반영할 때는 다른 표면을 유지한 채 API만 명시적으로 교체한다.
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\start-public-runtime.ps1 `
|
||||
-ForceApiRestart `
|
||||
-SkipEngineRestart `
|
||||
-SkipWebRestart `
|
||||
-SkipCloudflaredRestart
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
|
|
@ -113,6 +124,11 @@ and `C:\Users\<user>\.cloudflared\vignette-config.yml`.
|
|||
If engine health fails, verify that `claude` runs for the same Windows user that
|
||||
owns the scheduled task and that the user has completed Claude CLI login.
|
||||
|
||||
엔진 장애 중에도 `http://127.0.0.1:8001/health`의 `environment=prod`, `db=true`가
|
||||
유지되면 관리자·인증 제어면은 정상이다. 이때 watchdog은 API·웹·터널을 재시작하지
|
||||
않는다. 예약 작업 확인 기준은 `VignettePublicRuntime`의 `LastTaskResult=0`과
|
||||
`public-runtime-watchdog.failcount=0`이다.
|
||||
|
||||
If API health fails with `environment`, `db`, or auth configuration errors,
|
||||
inspect `apps/api/.env`; do not copy secret values into scripts or task
|
||||
arguments.
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@
|
|||
|
||||
```dotenv
|
||||
ENGINE_MODE=claude_api
|
||||
ANTHROPIC_API_KEY=sk-ant-... # 연구팀/기관 발급 키
|
||||
# ENGINE_URL 은 claude_api 모드에서 사용하지 않음 (host.docker.internal 라인 무시됨)
|
||||
ENGINE_URL=http://host.docker.internal:9099
|
||||
# ANTHROPIC_API_KEY는 엔진 게이트웨이 호스트에 연구팀/기관 발급 키로 주입
|
||||
```
|
||||
|
||||
- 코드는 `claude_api`를 **기본값으로 지원**한다(`apps/api/app/config.py`의 `engine_mode` 기본이 `claude_api`). 별도 개발 없이 env 전환으로 동작한다.
|
||||
- 코드는 `claude_api`를 게이트웨이 provider로 지원한다. 관리자는 `/admin/ai` 또는 `/settings`에서 게이트웨이가 Anthropic API로 조회한 모델과 지원 추론 강도만 선택할 수 있다. 키가 없거나 조회가 실패하면 해당 provider는 선택 불가 상태가 되고 잘못된 설정은 저장되지 않는다.
|
||||
- 비용: 회의 기록상 실사용 지난달 ~$10, 20명 액티브 시 최대 $100 이내 추정. 과금은 `ANTHROPIC_API_KEY` 계정으로 발생 → 연구팀/기관 계정 사용 권장. 관리자 UI에서 `ADMIN_USAGE_BUDGET_USD`로 예산 표시 가능.
|
||||
- `claude_cli` 게이트웨이(`apps/api/engine_gateway/`), 로컬 `claude -p` 상주풀, `ENGINE_READY_TTL_SECONDS` 튜닝은 **모두 불필요**해진다.
|
||||
- 개인 `claude` CLI와 상주풀은 불필요해지지만 `apps/api/engine_gateway/` 자체는 Anthropic API 모델 탐색·실행을 위해 계속 필요하다. 학교 서버 또는 별도 엔진 호스트에 게이트웨이를 실행하고 API 키는 그 프로세스에만 주입한다.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue