감정 관측 운영 검증과 문서 동기화

This commit is contained in:
Yun Chan 2026-09-23 05:03:35 +09:00
parent 29d5b6c50b
commit 2e4a6139f0
8 changed files with 209 additions and 8 deletions

View file

@ -86,3 +86,37 @@
### 2026-09-23 현재 runtime 재검증
[Runtime 재검증 영수증](../ops/evidence/jev-runtime-verification-2026-09-23.json)은 배포 컨테이너와 로컬의 Jev 관련 소스 4개 hash 일치, 과거 2회기의 9축 감정 상태와 OpenRouter audit 교집합, 그리고 독립 공유 adapter의 새 9축 호출(472ms)을 분리해 기록한다. 과거 audit은 새 호출이 아니며 독립 호출도 생성·DB·audit 저장을 검증하지 않는다. 구현·runtime 연결은 수용하되 한국어 품질·전체 지연·품질 승격 게이트는 계속 열린다.
## 관리자 감정 관측 콘솔 — 2026-09-23
`ADMIN-AFFECT-001`은 관리자 전용 읽기 화면 `/admin/emotions`로, 회기별 9축 상태와 완료된 내담자 턴의 Jev 전이 기록을 관측한다. 실제 사람의 감정 측정이나 진단이 아니며, 엔진·키·감정 정책을 조작하는 화면이 아니다. 기존 `/admin/ai`는 생성 응답 엔진 설정을 계속 소유하고, Jev는 서버의 provider·startup key 설정을 따른다. 두 화면은 메뉴와 안내에서 분리한다.
### trace 저장·격리 계약
- migration 23의 `app.client_affect_trace`는 `turn_id` 기본키와 `session_id`, `trace jsonb`, `created_at`을 보존하며 turn·session FK는 모두 cascade다. session/time index로 관리자 회기 목록과 역순 페이지를 조회한다. 기존 행을 역산하거나 변경하지 않는다.
- trace v1은 `schema_version=1`, 실제 provider/model·latency·token·cost, 상담 왕복 번호 `turn_seq`, 실제 전이와 같은 policy snapshot, 당시 코드 상태 context, 고정 순서의 9개 dimension을 저장한다. dimension은 `anxiety, sadness, anger, shame, guilt, loneliness, relief, hope, trust` 순서이고 각 항목의 `before/target/after/confidence/probabilities/decision`에서 결측은 0으로 바꾸지 않는다. 5개 확률은 강도 0~4 순서다.
- trace는 `TurnContext` 내부 필드다. 공개 `TurnResult`, stream `done`, `TurnRecord` JSON, `provider_events`와 학습자·교수자 DTO에는 넣지 않는다.
- Jev가 활성인 완료 내담자 턴만 client append와 같은 DB transaction에서 trace insert와 `state_after` upsert를 함께 실행한다. 실패하면 모두 rollback하며, 성공 뒤에만 process mirror를 갱신한다. legacy·위기·실패·취소 턴은 기존 동작을 유지하고 trace를 남기지 않는다.
- RLS는 관리자 SELECT만 허용한다. 서버 learner context의 INSERT는 연결된 client turn과 자기 session 일치가 전제이며 learner·teacher의 SELECT/UPDATE는 허용하지 않는다. 운영 스키마는 앱이 생성하지 않고 readiness 계약 실패를 명시 오류로 표면화한다.
### 관리자 API·표시 계약
- `GET /admin/affect/sessions?limit=1..100&offset>=0`은 최근 회기부터 `session_id/persona_code/started_at/ended/trace_count`만 반환한다. learner id·이름·메일은 반환하지 않으며 runtime의 `configured`는 연결 시험 성공을 뜻하지 않는다.
- `GET /admin/affect/sessions/{session_id}?limit=1..200&before_seq>0`은 현재 저장된 9축 상태, 연결된 내담자 turn의 `app.turns.seq` 기준 cursor 페이지와 `has_more`를 반환한다. trace의 상담 왕복 번호 `turn_seq`와 cursor 값은 같은 필드가 아니다. trace 없는 기존 회기도 조회할 수 있으며, 초기값을 합성하지 않는다. DB·스키마 오류는 503, 없는 회기는 404, 권한은 403, 잘못된 입력은 422다.
- 화면은 현재 상태 9축 막대, trace `turn_seq` gap을 끊어 보이는 9개 SVG 추이, 원자료 표, 선택 턴의 before→Jev target→after와 판단 확신도·5단계 분포·provenance를 제공한다. confidence는 정답 확률이 아니다. 자연어 판단 근거를 생성하거나 추정하지 않는다.
- 관계 행렬은 현재 불러온 trace의 `after-before`만 사용한다. 공통 유효 관측이 5개 이상이고 각 축 분산이 있을 때만 Pearson r을 표시하며, 그 밖에는 데이터 부족 또는 변화없음으로 표시한다. 이는 `상태 변화의 동행 · 인과관계 아님`이며 5개는 표시용 최소값일 뿐 통계적 유의성 기준이 아니다.
- 기존 앱 셸·Pretendard·의미 토큰·간격을 재사용한다. 데스크톱 추이는 3열, 모바일은 1열이고 320/390/1440px, dark/light, reduced-motion, keyboard, print를 검증 범위로 둔다. stale 요청은 AbortController 또는 요청 ID로 이전 회기 결과가 새 선택을 덮지 않게 한다.
### 현재 판정
backend trace/API와 실제 격리 PostgreSQL의 RLS·cascade·원자적 성공·rollback 검증은 수용했다. 실제 DB API 통합은 목록의 empty offset/total, JSONB trace decode, 상세 cursor·null·404를 확인했다. canonical artifact를 실제 재생성한 기술 검증은 6/6 통과, 외부 임상 승인은 0건이며 runtime package 8개 파일은 HEAD와 일치했다. migration 23의 운영 적용은 성공했고 RLS 2개 정책·grants·기존 행 0을 확인했다. `VIGNETTE_CLIENT_AFFECT_PROVIDER=legacy` baseline의 API 전체는 1191 passed·1 skipped·0 failed, gateway는 82 passed다.
웹 `generate:api-types`/`check:api-types`, typecheck, build, design SSOT와 focused 16개는 통과했다. 관리자 레이아웃 3개와 session-layout desktop/mobile 각 4개도 통과했고 실제 그래프·핵심 스크롤, dark/mobile/추이/관계 렌더 캡처와 API 운영 배포 증거를 수용했다. NAS web은 commit `29d5b6c5`, image `sha256:30cffa81e9c569a7ba93b7b90dd3b45459f79368d70675c83c041a9e4ec245b8`, entry `index-hrsPsc6h.js`, AdminAffect `AdminAffect-CXdULwAE.js`로 build와 컨테이너 교체 모두 exit 0이며 API·engine·DB 컨테이너는 유지됐다. 이에 `ADMIN-AFFECT-001`은 운영 수용 완료다. 실제 1턴 trace의 관계 표시는 데이터 부족이며, fixture의 5개 이상 관계 계산 검증과 혼동하지 않는다. 전체 `layout-visual-gate`는 15개 중 1 passed·1 failed·13 not run 상태이므로 전체 GREEN으로 주장하지 않는다. 720px prestart의 `회기 시작`은 ancestor clip(`left=25`, `right=695`)이 baseline `d22cd988`과 현재 작업트리에서 동일하게 재현되어 감정 관측 기능의 원인으로 단정하지 않는다.
### 운영 수용 기록 보강
[ADMIN-AFFECT 운영 영수증](../ops/evidence/admin-affect-runtime-2026-09-23.json)은 API commit `6ab40ff3`와 image SHA-256 prefix `2c437011`, migration 23의 RLS 2개 정책, API health `ok`·DB/engine 정상, Pages commit `29d5b6c5`의 공개 entry `index-m3-R1olx.js`·AdminAffect JS/CSS·기존 사용자 bundle `index-BA_Q0J7Y.js`의 200, health 200을 기록한다. 인증 CUA에서는 새 합성 회기 1왕복 뒤 관리자 화면의 trace 1개, 9축, 5단계 분포, 반영/보류와 policy/context, 실제 `openrouter`/`typesafe/jev-1.13-20260917` 응답의 492ms·input 2273·output 128·$0.000095466을 확인했다. 9개 SVG, ready 1, 진단 0, console error 0 및 회기 종료 뒤 2발화 리뷰 평가까지 확인했고, 새로고침 뒤에도 종료 회기 trace 1개·9축·5단계 분포를 복원했다. 사용자·합성 회기 식별자와 대화 원문은 기록하지 않는다.
운영 DB 읽기 전용 검증은 `BEGIN READ ONLY` 뒤 `ROLLBACK`으로 trace 1개, 연결 client turn FK 일치, trace shape와 9축 key, 실제 provider/model·492ms, 저장 current state와 trace `after` 일치, 회기 종료 상태를 확인했다. 쓰기나 원인 추정은 하지 않았다.
보존 census는 133개 테이블 중 131개 digest 일치, `app_user`·`auth_session` 행 수 동일과 digest 변경(로그인 활동은 있었으나 원인은 확정하지 않음), uploads 93개·52,973 bytes digest 일치, engine/DB 컨테이너 동일을 보인다. dark/mobile/추이/관계 캡처와 렌더 뒤 3.6/8.1초 관측 구간의 진단 0, NAS web 컨테이너 교체 exit 0을 수용했다. watchdog은 공통 root marker 누락으로 한 차례 반려한 뒤 보완해 고정 수용했으며, 영수증은 임시 scratch 원시로그와 CUA 관측을 root가 검토해 요약한다. fixture·API·실제 runtime 증거 경계를 구분한다.