vignette/docs/ops/backlog-2026-06-26.md

79 lines
18 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.

# Vignette 백로그 — 2026-06-26
이 세션(레이아웃 시각 수용 마감 + live 운영 증거 라운드) 이후 남은 항목을 한곳에 모은다.
대시보드(`docs/dev_dashboard.html`)의 상태와 일치한다. 운영 원칙상 가짜 증거로 DONE 표기하지 않으므로,
실증/외부 의존/소유자 결정이 필요한 항목은 여기서 추적한다.
분류: **B1 비차단 폴리시** · **B2 환경 제약(증거 생산 불가)** · **B3 소유자 결정** · **B4 외부 거버넌스**
> **B0. 원천문서 갭 분석 (2026-06-26 추가)** — 한신대 산학협력 원천문서 5종 정독으로 도출한 "부족한 부분"(critical 3 / high 4 / medium+ 6)은 **SSOT 대시보드** `docs/dev_dashboard.html` "원천문서 갭 분석" 섹션과 상세 `docs/ops/source-docs-gap-analysis-2026-06-26.md`에서 추적한다. C1 사례개념화 산출물, C2 위기개입 프로토콜, C3 이론모드 스레딩은 1차 구조를 만들었다. 콘텐츠 정의는 임상팀(구훈정·어유경) 소유라 코드는 구조를 선제 구축하되 임상 문안과 평가기준은 외부 정의로 받는다.
---
## B1. 비차단 코스메틱 폴리시
- [x] **공용 셸 사이드바 구분선 전체 높이** — (2026-06-26 처리) `.vg-nav border-right`(sticky·뷰포트 높이)를 제거하고, `.vg-shell__body` 컨테이너 배경 하어라인(`linear-gradient` 1px×100%, `--nav-cur`로 그리드·구분선 폭 동기화)으로 본문 그리드 전체 높이를 따라가는 세로 구분선을 그린다. 1024 축소폭은 `--nav-cur: var(--nav-w-collapsed)`로, 720 이하(상단 가로바 전환)는 `background-image:none`으로 정합. `components/shell/shell.css`. **검증: `npm run typecheck` PASS + `vite build` PASS.** 시각 회귀 게이트(gate 7/7 + 포커스 E2E + session-layout 8/8)는 web+api+DB 스택(Docker 미기동·secrets 부재)으로 이 환경 미실행 — 스택 가용 시 1회 시각 확인 권장.
- [ ] **빈상태 컬럼 높이 여백** — session-review 1280 masonry 우측 컬럼, session-prestart 우측 '진행 초점' 컬럼, learner-home 1440 하단 등 데이터 0건 빈상태에서만 두드러지는 컬럼 높이 불균형/여백. 실데이터면 채워진다. **(미착수, 의도적)** 0건 전용 시각 균형이라 러닝 앱에서의 시각 반복 없이 블라인드 수정 시 악화 위험 → 스택 가용 시 시각 확인하며 처리. 빈상태 전용 균형 레이아웃 채택 자체가 별도 폴리시 결정.
- [x] **learner-home 로딩 스켈레톤 밀도(390)** — (2026-06-26 처리) '연습 대상' 로딩 스켈레톤을 빈 div 단일 셰이머에서 실제 카드 구조(마크 박스 + 이름/메타/요약 2줄 플레이스홀더)를 모사하도록 밀도 보강. `LearnerHome.tsx` (`.lh-skel__box`/`.lh-skel__line*`), reduced-motion 분기 동반 갱신. **검증: `npm run typecheck` PASS + `vite build` PASS.**
검증 기준: 변경 후 `npm run typecheck`, `e2e/layout-visual-gate.spec.ts`(7/7), 레이아웃 포커스 E2E(54), `e2e/session-layout.spec.ts`(8/8) 무회귀. (E2E 게이트는 web+api+DB 스택 필요 — 현재 워크스테이션은 Docker 미기동·API secrets 부재로 미실행. 코드 변경은 typecheck+vite build로 1차 검증함.)
---
## B2. 환경 제약 — 이 워크스테이션에서 증거 생산 불가
- [ ] **`vnet.18ka.net` 공개 live 전환** — 코드/스크립트는 `vnet.18ka.net`·`api-vnet.18ka.net`을 허용하도록 갱신 완료. Tailnet 주소 `https://alpaca-home.taile93291.ts.net`은 dev-login까지 검증됐고, public OAuth redirect가 로컬/Tailnet 세션으로 붙지 않는 혼선을 막기 위해 Tailnet Google 버튼과 직접 Google 시작 URL은 `local_oauth_unavailable`로 차단한다. 공개 vnet은 현재 (1) `vnet.18ka.net`/`api-vnet.18ka.net` DNS 부재, (2) cloudflared 현재 인증 컨텍스트가 `18ka.net` zone이 아니라 `chanpaca.net` zone에 route dns를 생성, (3) Google Console redirect URI `https://api-vnet.18ka.net/auth/callback` 등록 필요 때문에 live 완료 아님. 현재 prod API 8001 자체는 `db:true`, `engine:true`로 건강하다. 상세: `docs/ops/tailscale-vnet-runtime-2026-06-27.md`.
- [ ] **공개 Google OAuth 실제 `/turn` proof** — 로그인 가능한 계정으로 `storageState` 캡처 후 `E2E_PUBLIC_AUTH=1` + `chromium-public-auth` 1회 통과 필요. (소유자 지시로 이번엔 보류.)
- [ ] **음성 캐스케이드 live** — 실제 Deepgram STT / OpenAI TTS provider 키, 물리 마이크, 공개 WSS 50분 양방향 실측. provider 키/하드웨어 부재로 미실행(백엔드 계약·UI synthetic 경로는 단위/E2E로 증명됨).
- [ ] **claude_cli ↔ Messages API 폴백 동일성**`ANTHROPIC_API_KEY`가 있어야 Messages API 경로를 돌려 동일성 검증 가능. claude_cli 경로는 게이트웨이 probe로 live 실측 완료.
- [ ] **저항엔진 openness 곡선 DB 실증** — fast-loop 평가의 `client_state_read``app.turn_client_state` 적재 경로는 추가됨. 다만 dev DB 실데이터/다수 실턴(real LLM) 기반 openness 곡선 증거는 아직 필요. 현재는 `app.test_state_machine_resistance` 단위테스트와 `app.test_evaluation_persistence` 매핑 테스트로 1차 증명.
- [ ] **재부팅 후 watchdog smoke**`watch-public-runtime.ps1` + Scheduled Task가 재부팅 후 엔진/API/터널을 복구하고 public `/turn`이 통과하는지 실측. 재부팅 불가로 미실행(parser/ check-only 경로는 확인됨). 2026-06-27 점검에서 아직 DNS가 없는 `api-vnet.18ka.net`은 기본 watchdog 검사에서 제외했고, 개통 후 `-AdditionalPublicHealthUrls`로 명시 추가하도록 runbook을 보강했다.
- [ ] **상주 엔진풀 RSS 실측** — probe로 TTFT/세션재사용/cost는 live 확인. 프로세스 RSS(메모리)는 별도 OS 계측 항목으로 남김.
---
## B3. 소유자(윤찬) 결정 필요 — 단독 결정 시 월권
> 결정되면 대시보드 `결정 필요` 탭과 관련 Phase 항목을 갱신한다.
- [ ] **s2s 2차 PoC 채택 여부** — 캐스케이드만 / s2s 2차 PoC 유지. 영향: 음성 아키텍처·전송 비용·로그 보존. 기준표 `docs/decisions/voice-s2s-poc.md`.
- [ ] **서비스용 페르소나 음성 모델·권리 정책** — (2026-06-27) P1 서연 음성 아트 PoC는 Higgs v3 무참조 synthetic seed 방식으로 생성(`docs/voice-art/p1-seoyeon-higgs-v3-20260627/`). 실존 인물/성우 reference 사용 금지 확정. `VIGNETTE_VOICE_POC_SAMPLE_TTS=true` 개발 플래그로 `/voice/ws` P1 sample TTS provider에 연결했지만, 서비스 배포 전 선택 필요: Higgs 상업 라이선스 확보 / 동의 받은 reference voice만 사용 / 배포 가능한 대체 TTS provider 채택.
- [ ] **fast-loop 외부 API 허용** — 로컬 전용 / 외부 Haiku·Solar 폴백 허용. 영향: 데이터주권·레이턴시·장애 대응.
- [ ] **재귀학습 fine-tuning 범위** — few-shot 자동갱신만 / fine-tuning 포함. 영향: 동의서·데이터셋 품질·운영 리스크.
- [ ] **자유연습 기본값** — 기본 ON(+피드백 권장 배지+교수자 토글) / 교수자 승인 후 ON. 영향: 학습자 자율성·평가 품질·안전 정책.
- [ ] **추가 축어록 수급 규모·일정** — 목표 N건/일정 확정. 영향: 평가 타당도·페르소나 다양성·Phase 3 측정력.
---
## B4. 외부 거버넌스 — 한신대/데이터 steward 서면 증거
- [ ] **한신대 데이터/SSO 거버넌스 게이트** — SSO claim mapping, 추가 축어록 수급, 미성년 원본 활용동의, 개인정보 처리방침. 로컬 문서 골격 `docs/ops/hanshin-data-governance-gate.md` 준비됨, 외부 서면 증거 필요. (소유자 지시로 이번엔 보류.)
---
## Phase 3 파일럿 게이트 (실참여자 필요)
- [ ] 20명 교육용 파일럿 운영 / 효과성·KPI 측정(SUS·자기효능감·κ/ICC·환각률) / 재귀학습 데이터셋 approved 산출 / 개인정보·동의 감사. 문서/checker와 dry-run exporter는 준비됨(`docs/phase3/*`, `scripts/check-phase3-artifacts.py`, `scripts/export-recursive-dataset.py`), 실제 approved evidence는 파일럿·steward/legal 검수·IAA 통과 시점.
---
## 이번 세션에 닫은 것(참고)
- 레이아웃 시각 수용: 게이트 7/7 + 적대적 재검수 7/7 accept + 코스메틱 폴리시(공용 셸 1건 제외) — DONE.
- live 운영 증거: engine config 운영값, 상주 엔진풀 probe(TTFT/cost/세션재사용), Postgres RLS/audit smoke 5 checks PASS, turn cost telemetry(app.turns 13행) — DONE.
- P1 서연 음성 아트 PoC: Higgs v3 무참조 synthetic seed + 5개 정서/속도 변주(mp3/wav) 생성. 실존 reference voice 미사용. `/voice/ws`에는 dev-only sample TTS provider로 연결했고 live text_turn smoke에서 `p1-sample-poc` binary 20 chunks/78,573 bytes 수신 확인. 이 provider는 TTS override만 담당한다(마이크/STT는 OpenAI 키 필요). 프로덕션 탑재는 라이선스/권리 결정 전 금지.
- 부수: RLS smoke 스크립트 cleanup RLS 컨텍스트 버그 수정, `vignette_app`(NOBYPASSRLS) dev 역할 프로비저닝.
- 평가 SSOP 2차: `TurnRecord.evaluation``app.feedback_scores`/`turn_technique`/`turn_client_state`/`supervisor_comment`/`alternative_utterance`로 정규화 적재·복원하고, submit/stream/voice 저장 경로를 `app.turn_runtime`으로 공통화했다. 원시 평가 테이블은 evaluator/admin RLS로 제한하고 서버가 evaluator 컨텍스트로 적재한다. `app.turns` insert는 `RETURNING id` 확인으로 silent success 제거. **검증: `pytest app/test_evaluation_persistence.py -q` 6 passed, 관련 회귀 45 passed, `pytest app/` 119 passed, `pytest engine_gateway/` 7 passed.**
- 계약 SSOT 1차: FastAPI OpenAPI를 `apps/web/src/lib/api.gen.ts`로 생성하는 `npm run generate:api-types`와 stale 체크용 `npm run check:api-types`를 추가했다. `TurnResponse`/`SessionEndResponse`는 생성 타입 alias를 사용해 기존 `crisis_kind`/`end_state` 드리프트를 닫았다. 나머지 수기 미러 DTO 전환과 CI 연결은 후속. **검증: `npm run check:api-types`, `npm run typecheck`, `pytest app/` 119 passed, `pytest engine_gateway/` 7 passed.**
- 위기게이트 109 1차: 실제 자해·자살 신호는 LLM/엔진 호출 전 중단하고 `crisis_resource(109)``conversation_stopped`를 REST/SSE/voice 응답에 싣는다. `app.safety_events` detail 적재와 교수자 대시보드 안전 알림 큐까지 연결했다. 안전 이벤트 DB insert payload도 회귀화했다. 임상 문안, 실시간 push/메일, 지역 자원 확장은 후속. **검증: `pytest app/test_session_turn_persistence.py app/test_voice_ws.py -q` 20 passed, `pytest app/test_teacher_dashboard.py app/test_session_turn_persistence.py app/test_voice_ws.py -q` 21 passed.**
- 교수자 성장 추적 1차: `teacher_dashboard`가 정규화된 턴 평가를 기반으로 학습자별 적절성·라포·기술 사용 추이를 집계하고, 교수 콘솔에 최근 회기/항목별 성장 카드와 안전 알림 큐를 함께 표시한다. **검증: `pytest app/test_teacher_dashboard.py app/test_session_turn_persistence.py app/test_voice_ws.py -q` 21 passed, `pytest app/` 119 passed, `npm run check:api-types`, `npm run typecheck`, `npm run build`.**
- 페르소나 저작 CRUD 1차: teacher/admin 전용 `POST/GET/PUT /personas/drafts` 경로를 추가해 draft 생성·조회·편집·검수요청을 열고, 교수 콘솔에 JSON 초안 작성/불러오기/수정 UI를 연결했다. 승인된 카드만 학습자 catalog에 노출하는 기존 경계는 유지한다. P4~P7 실제 임상 콘텐츠 입력은 후속. **검증: `pytest app/test_persona_review.py -q` 19 passed, `pytest app/` 119 passed, `npm run check:api-types`, `npm run typecheck`, `npm run build`.**
- M1 비언어 이벤트 1차: voice learner turn에 이미 저장하던 `audio_ref`/`silence_ms`/`speech_rate`/`barge_in`을 리뷰 API `nonverbal` 이벤트로 노출하고, 회기 리뷰 축어록에 침묵·발화 속도·끼어듦·음성 입력 칩을 표시했다. 한숨·울음 감지와 live 마이크/STT/TTS 실측은 후속. **검증: `pytest app/test_session_turn_persistence.py app/test_voice_ws.py -q` 17 passed, `npm run check:api-types`, `npm run typecheck`, `npm run build`, `session-review.spec.ts` 1 passed.**
- M2 다회기 케이스 아크 1차: DB 세션 생성 시 `(persona_id, learner_id)` 기준 `case_profile`을 upsert하고, `session_no`를 트랜잭션 안에서 원자 증가시키며, `InProcSession.case_id`가 매회 새 `runtime_case_id`가 아니라 안정 `case_id`를 가리키게 수정했다. 시작/submit/stream/voice 경로는 직전 `session_summary` 기반 seed recall을 사용한다. `case_digest`/`pinned_fact` 실적재, episodic embedding writer, trajectory 갱신은 후속. **검증: `pytest app/test_session_turn_persistence.py app/test_voice_ws.py -q` 18 passed, `pytest app/ -q` 119 passed, `npm run check:api-types`.**
- C1 사례개념화 워크시트 1차: `SessionReviewResponse.caseWorksheet`와 리뷰 화면 read-only 카드가 탐색 11항목·호소 5영역·인지삼제/감정·보호/방해·생물심리사회 목표 초안을 축어록 근거 기반으로 제공한다. 편집·DB 저장, 임상 루브릭, AI 추출/채점, 교수자 검수는 후속. **검증: `pytest app/test_session_turn_persistence.py -q` 15 passed, `pytest app/ -q` 119 passed, `npm run check:api-types`, `npm run typecheck`, `npm run build`, `session-review.spec.ts` 1 passed.**
- C3 이론모드 1차: `theory_mode``TurnContext`/sessions/voice/evaluator뿐 아니라 `build_turn_messages`의 엔진 메시지까지 전달된다. 프론트는 `persona.theory_target` 기준으로 시작해 `humanistic` 하드코딩을 제거했다. CBT 체인·이론부합 루브릭·명시적 선택 UI는 후속. **검증: `pytest app/test_orchestrator_masking.py app/test_session_turn_persistence.py -q` 20 passed, `pytest app/ -q` 119 passed.**
- M3 인증 claim 1차: Google/SAML/dev-login이 `AUTH_EMAIL_COHORT_MAP`·`AUTH_DOMAIN_COHORT_MAP` 및 SAML cohort claim을 `cohort_ids`로 전달하고, DB `app_user.external_id`는 provider subject(`google:`/`saml:`/`dev:`) 기반으로 저장한다. 운영 SAML 서명검증, 기관 claim schema/test tenant, deprovisioning audit은 후속. **검증: `pytest app/test_auth_providers.py -q` 22 passed, `pytest app/ -q` 119 passed.**
- X2 예산 경고/저비용 평가 라우팅 1차: `ADMIN_USAGE_BUDGET_USD` 설정값을 기준으로 `GET /admin/usage``budget.status=disabled|ok|warn|exceeded`, 사용률, 잔여 예산을 반환하고 관리자 콘솔이 예산 상태 배너를 표시한다. 80% 이상 warn, 100% 이상 exceeded. `EVALUATOR_FAST_MODEL`/`EVALUATOR_DEEP_MODEL` 설정 시 fast/deep 평가 호출만 해당 모델 override로 gateway에 전달하고, 비워두면 기존 gateway default 라우팅을 유지한다. semantic cache·장기 한도 정책은 후속. **검증: `pytest app/test_evaluator_model_routing.py app/test_runtime_policy.py -q` 23 passed, `pytest app/ -q` 122 passed, `npm run check:api-types`, `npm run typecheck`.**
- H4 LLM call audit 1차: 상담 생성(generate/stream)·fast-loop 평가·deep-loop 평가의 외부 LLM 호출 직후 `audit.llm_call_log`에 provider/model/token/cost/inference_geo/latency metadata만 적재한다. prompt/completion 본문은 저장하지 않고 감사 실패는 상담 루프를 막지 않는다. 로컬 dev-login 실제 `/turn` smoke에서 `audit.llm_call_log`가 8→11로 3행 증가했다(session `2460d56c-a9cb-4a40-a175-9575d510a5e9`). 공개 Google OAuth 실제 `/turn` proof는 별도 B2 항목에 남긴다. **검증: `pytest app/test_orchestrator_masking.py app/test_evaluation_persistence.py app/test_session_turn_persistence.py -q` 27 passed, `pytest app/ -q` 119 passed.**
- 공개/로컬/Tailnet 로그인 복구: public API 530 원인은 prod에서 개발 전용 `VIGNETTE_VOICE_POC_SAMPLE_TTS=true`가 fail-close된 것과 DB `app.admin_engine_config` 기본 행 부재였다. `scripts/start-public-runtime.ps1`는 public prod 기동 시 샘플 TTS를 강제로 끄고, 운영 DB에는 `claude_cli`/`127.0.0.1:9099` engine config 행을 복구했다. 추가로 dev/Tailnet에서는 public OAuth callback이 로컬/Tailnet 세션으로 붙지 않으므로 Google 버튼과 직접 시작 URL을 `local_oauth_unavailable`로 차단하고 dev-login만 사용한다. public OAuth state는 HttpOnly 쿠키에 묶인 HMAC 서명 토큰으로 복구해 API 재시작 뒤 콜백이 `invalid_state`로 떨어지지 않는다. callback 실패는 비밀값 없는 reason/status로 남기고, 로그인 화면도 token/state/provider/identity 실패 메시지와 reason code를 분리한다. **검증: `https://vignette.chanpaca.net/login` 200 + `index-BzQmv-jU.js`, `https://api-vignette.chanpaca.net/health` prod/db/engine true, public Google auth redirect 302 + HttpOnly state cookie, provider callback error → `/login?oauth=access_denied`, local/Tailnet Google direct는 각 origin의 `/login?oauth=local_oauth_unavailable`, local auth E2E 7 passed, Tailnet auth/dev-login E2E 2 passed.**
- Docker dev DB 안전장치: `scripts/dev-up.ps1``vignette-dev-db` healthcheck/pg_isready/role safety를 점검하고, 새 컨테이너 생성 시 `vignette_owner`와 앱 role을 분리해 healthcheck를 붙인다. `infra\.env` compose 필수값 누락도 사전 경고한다. `scripts/dev-down.ps1`은 기본 DB 보존, `-Db` 지정 시 컨테이너 중지로 정리했다. **검증: 현재 Docker DB accepting connections, `vignette_app:false:false`, 기존 컨테이너 healthcheck 없음. 추가 확인 결과 기존 DB는 `POSTGRES_USER=vignette` 기반이고 API startup DDL이 owner 권한을 요구해 런타임 app-role 전환은 마이그레이션 owner/런타임 role 분리 후 진행해야 한다.**