122 lines
24 KiB
Markdown
122 lines
24 KiB
Markdown
# Jev 기반 가상 내담자 감정 상태
|
||
|
||
결정일: 2026-09-22. 상태: 감정 경로 구현, OpenRouter 실제 판단 검증, 신규 artifact 보존 갱신·전체 회귀, 운영 API 배포와 합성·인증 브라우저 3턴 runtime을 수용했다. 한국어 품질 승격과 사용자 체감 지연 개선은 미완료다.
|
||
|
||
## 문제와 목표
|
||
|
||
기존 내담자는 페르소나·대화 기억·개방도·저항을 전달받지만, `SessionState.affect_state`를 턴마다 갱신하는 경로가 없다. 감정 저장 통로가 있는 것과 감정이 대화에 따라 변화하는 것은 다르다. 이번 변경은 감정의 지속성·혼합·변화를 실제 대사 생성에 연결한다. 사례 사실의 일관성, 감정의 개연성, 응답 지연은 별도로 평가한다.
|
||
|
||
## 근거와 적용 범위
|
||
|
||
- [TypeSafe 소개](https://docs.typesafe.ai/introduction): Jev는 자유 문장을 생성하지 않고 구조화된 선택·점수·확률을 반환한다. 질문별 독립 판단을 한 요청에 묶을 수 있다.
|
||
- [OpenRouter Decisions API](https://openrouter.ai/docs/api/api-reference/alphadecisions/submit-a-decisions-questions-and-answers-request): 사용자가 선택한 `POST https://openrouter.ai/api/alpha/decisions`, Bearer 인증, `state`·`model`·`questions`와 `answers`·`usage` 계약을 사용한다. 채팅 생성 endpoint를 사용하지 않는다.
|
||
- 사용자 지정 모델은 `~typesafe/jev-latest`다. 요청 식별자를 그대로 보내며 응답의 실제 모델을 별도로 기록한다. [모델 문서](https://docs.typesafe.ai/models)의 언어 제약에 따라 한국어 품질은 별도로 검증한다.
|
||
- [공식 발표](https://typesafe.ai/blog/introducing-system-one-models-and-jev)의 속도 수치는 회사 자체 평가다. Vignette 전체 응답 속도나 임상 정확도의 증거로 사용하지 않는다. 타입이 올바른 출력도 의미적으로 틀릴 수 있다.
|
||
- [Appraisal 기반 감정 에이전트 연구](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0301033)는 사건의 개인적 의미를 정서 표현에 연결하는 참고 근거다. 게임 에이전트 결과를 상담 타당성으로 확대하지 않는다.
|
||
- [EmoCharacter](https://aclanthology.org/2025.naacl-long.316/)는 역할 재현과 감정 충실도가 별도 평가 대상임을 보여 준다. 큰 모델이나 페르소나 프롬프트만으로 감정 품질이 보장되지 않는다.
|
||
|
||
## 확정 구조
|
||
|
||
마스킹된 페르소나·기억·현재 상담자 발화 → Jev의 병렬 감정 판단 → 코드가 소유하는 제한된 상태 전이 → 기존 대화 모델의 스트리밍 발화 순서다. 기존 위기 게이트는 Jev보다 먼저 적용한다. Jev가 위기 판단·라포·단계 전이·진단·교수자 평가를 대신하지 않는다.
|
||
|
||
9개 감정은 불안·슬픔·분노·수치심·죄책감·외로움·안도·희망·신뢰다. 각 감정의 강도를 독립적으로 유지하므로 안도와 죄책감, 희망과 불안이 동시에 높을 수 있다. 선택지 확률을 감정 강도로 오인하지 않고, 감정별 5단계 `Score`를 정규화한다.
|
||
|
||
영속 키는 `emotion_<dimension>`이며 기존 `affect_state` JSON과 회기 종료 snapshot을 사용한다. 기존 임상 키는 보존한다. 이전 값이 없으면 명시된 감정 기저선, 불안 기저선, 부정 정서, 무망감에 대응하는 희망값을 사용한다. 근거가 없는 다른 축의 초기값은 0이다. 이 초기화는 공학적 시작값이며 임상적으로 보정된 척도가 아니다.
|
||
|
||
높은 confidence의 전이는 `old + clamp(0.35 × (target − old), −0.15, +0.15)`다. 기본 문턱 0.65 미만은 아래 후속 개선의 분포 조건에 따라 작은 잠정 전이 또는 보류로 나눈다. 이 상수들은 검증 전 공학적 기본값이며 전문가 평가로 보정해야 한다. Jev가 판단한 값을 DB 상태에 직접 덮어쓰지 않는다.
|
||
|
||
감정은 대사·주저함·침묵·말투에 반영하되 수치와 내부 제어문을 발화하지 않도록 한다. 고정 사실과 사례 설정을 바꾸지 않으며, 내담자가 상담사 역할로 전환하지 않는다. 기존 시나리오 지시와 개방도 제약도 유지한다.
|
||
|
||
## 지연·장애·개인정보 계약
|
||
|
||
- 기존 `httpx` 연결을 재사용하고 9개 판단은 한 요청으로 묶는다. 기본 전체 deadline은 1.2초이며 자동 재시도는 하지 않는다.
|
||
- Jev 판단은 발화 전에 필요하므로 이 단계 자체는 지연을 추가한다. 전체 응답이 빨라졌다는 주장은 실제 첫 토큰·전체 응답시간의 기존 경로 대비 측정 없이는 하지 않는다.
|
||
- `VIGNETTE_CLIENT_AFFECT_PROVIDER=legacy|jev`로 명시적으로 선택한다. 기본은 기존 경로다. Jev 선택 시 키 누락·시간 초과·잘못된 응답을 다른 공급자로 숨겨 대체하지 않는다.
|
||
- `VIGNETTE_JEV_PROVIDER=openrouter|typesafe`의 기본은 `openrouter`이며 `OPENROUTER_API_KEY`를 사용한다. 직접 TypeSafe 연결은 명시적 선택과 별도 키·모델이 있어야 한다. 공급자와 모델을 자동 변경하지 않는다.
|
||
- OpenRouter가 confidence를 생략하면 `None`으로 보존하여 실제 0과 구분하고 해당 축을 보류한다. 선택적 legend와 확률분포는 제공될 때 검증하며, 원본 5수준 확률을 보존한다. 비용은 응답의 실제 값을 기록하고, 없으면 미상으로 남긴다.
|
||
- 실제 수련생 위기에는 외부 감정 판단을 호출하지 않는다. 실패하거나 취소된 턴의 새 감정은 영속화하지 않는다.
|
||
- 외부로 보내는 페르소나·기억·발화의 모든 텍스트를 개인정보·역할 이름 마스킹 경계에 통과시킨다. 계정·세션 식별자, 교수자 평가, API 키를 입력이나 로그에 넣지 않는다.
|
||
- 실제 모델·판단 지연·사용량·수용/보류 차원의 내부 provenance와 공개 학습자 응답을 분리한다.
|
||
|
||
## 검증과 승격 조건
|
||
|
||
계약 검증은 전이의 관성·복합 감정 유지·회기 이월·마스킹·위기 우선·취소·실패 시 미저장·generate/stream 일치를 확인한다. 테스트 대역을 사용한 결과는 실제 Jev 성능 증거가 아니다.
|
||
|
||
실측 러너의 합성 한국어 입력은 API 연결과 판단 결과를 수집하기 위한 자료다. 스스로 만든 정답으로 정확도를 선언하지 않는다. 2026-09-22 OpenRouter 실측은 8개 사례를 2회씩 호출하여 16/16 성공, 판단 지연 p50 252ms·p95 358ms, 실제 모델 `typesafe/jev-1.13-20260917`, 응답에 보고된 총비용 $0.001519896이었다. 144개 축 중 71개가 confidence 0.65 이상이었다. 로컬 증거는 `scratch/jev/openrouter-korean-live-verified.json`이다. 이 결과만으로 전체 대화 품질이나 속도가 검증되지는 않는다.
|
||
|
||
실측 초기에 2자리 반올림으로 확률합이 0.99인 정상 응답을 거부하는 문제가 발견됐다. 5수준의 반올림 최대 합산 오차(5 × 0.005)만 허용하도록 보정한 뒤 위 실측을 다시 수행했다. 각 확률의 범위와 차원 계약은 유지한다. 작업자 구현은 오케스트레이터가 diff를 읽고 어댑터 20 passed와 감정·마스킹 회귀 48 passed를 근거로 수용했다. 테스트 대역과 실제 호출 증거는 구분한다.
|
||
|
||
실제 대화 연결은 `scripts/probe-jev-dialogue.py --output scratch/jev/dialogue-live.json --turns 2`로 수집했다. 기존 경로와 Jev 경로 각각 2턴이 완료되고 합성 세션이 정리됐다. 기존 경로 첫 토큰 지연은 5972/2684ms, Jev 경로는 6873/3368ms였다. 기존 출력 검증 버퍼 때문에 첫 토큰 시각은 전체 완료와 거의 같다. 순서를 고정한 소수 사례이므로 성능 비교 결론이나 정확도 주장은 하지 않으며, 이번 관측에서 Jev가 전체 응답을 빠르게 만들지는 않았다.
|
||
|
||
2026-09-22 신규 artifact 보존 갱신 뒤 전체 회귀는 API 1172 passed·1 skipped·0 failed, gateway 82 passed, web typecheck·build·API 타입 동기화 통과다. 현재 작업트리의 위기 기술 검증은 `scratch/jev/current-crisis-technical-observations.json`에 별도로 수집해 6/6 통과를 확인했고, 임상 외부 판정은 0건이다. 이전 미커밋 작업트리와 고정 증거의 지문 불일치는 역사 기록이며 현재 차단 항목이 아니다.
|
||
|
||
전체 사용자 흐름 검증과 품질 승격에는 동일 조건의 기존 경로 대비 충분한 다중 턴 비교, 사례 사실 모순율, 역할 이탈, 감정 변화와 혼합의 전문가 검토, 첫 토큰 및 전체 응답의 p50/p95, 장애율이 필요하다. 로컬 연결 검증과 품질 승격을 구분한다. 2026-09-22 운영 API 배포 뒤 공개 main JS `index-BA_Q0J7Y.js` 200, health 200(db·engine true), Jev configured, 비인증 `/auth/me` 401, `/sessions` OPTIONS preflight 200과 정확한 origin의 credential CORS를 확인했고, health의 `live_verified=false`는 의도된 상태 플래그로 실제 Jev 판단은 별도 probe로 확인했다. 이어 Google 인증 P13 회기에서 3왕복·6발화를 실제 완료하고 새로고침 뒤 6개 복원, 종료 review의 실제 축어록·6발화 기반 요약·턴 직후 평가와 후속 deep-loop 완료 표시, DB `turn_seq=3`·6 turns·종료 시각·9축 `affect_state` 영속화를 확인했다. 배포 중 일시 502 관측이 있어 무중단을 주장하지 않는다. 배포 영수증은 [운영 증거](../ops/evidence/jev-production-deploy-2026-09-22.json)에 보존한다.
|
||
|
||
## 후속 개선 설계 — 2026-09-22
|
||
|
||
[공식 confidence 설명](https://docs.typesafe.ai/confidence)에 따르면 confidence는 정답 확률이 아니라 결과 분포의 집중도를 요약한다. [Jev 1.13의 제한](https://docs.typesafe.ai/model-jaggedness/jev-1.13)은 점수를 실제 강도의 정밀 측정치로 보지 말라고 명시한다. 따라서 인접 강도 사이의 불확실성과 서로 먼 강도 사이의 불확실성을 구분한다.
|
||
|
||
후속 구현 계약은 다음과 같다. 높은 confidence의 기존 전이는 유지한다. confidence가 0.35 이상이고 높은 문턱보다 낮을 때, 유효한 분포를 정규화한 뒤 인접 두 수준의 확률 합이 0.80 이상인 경우에만 작은 잠정 전이(`alpha=0.15`, 최대 변화 0.075)를 허용한다. confidence 누락, 낮은 confidence에서 넓게 퍼진 분포나 양끝으로 나뉜 분포는 보류한다. 잠정 차원은 내부 메타데이터에 별도로 기록한다. 이 값들은 합성 연기의 공학적 정책이며 보정된 임상 기준이 아니다.
|
||
|
||
생성 입력은 기존 임상 상태를 보존하면서 새 감정의 긴 소수점 목록을 질적 강도와 짧은 연기 지시로 바꾼다. 최대 네 감정을 전달하되 반대 정서가 함께 존재하면 이를 남긴다. 고정 사실과 충돌하는 상담자의 회상 유도를 과거 기억으로 받아들이지 않도록 지시한다. Jev 질문은 감정 주체, 근거, 지시문 주입 방어와 고정 사실 우선순위를 보존해 압축한다.
|
||
|
||
비교는 같은 합성 발화와 고정 사실, 같은 생성 모델 설정으로 수행한다. 각 반복에서 기존/Jev 순서를 교대하고 첫 턴과 이후 턴을 분리한다. 사실 충돌 사례는 명시적인 기대 행동과 원문을 남기며 자동 정확도 점수로 포장하지 않는다. 전체 출력 검사, 위기 게이트, 모델·effort 선택, 오류 전파 계약은 유지한다.
|
||
|
||
### 후속 구현 판정과 실측
|
||
|
||
오케스트레이터는 어댑터·감정 전이·측정 러너의 diff를 직접 검토해 수용했다. 측정 러너의 첫 턴/이후 턴 구분 오류와 전이 테스트의 불일치 입력은 재작업 후 수용했다. 지시문 9개의 문자 합계는 2,942→2,186으로 25.7% 줄었다. 같은 8개 fixture를 2회씩 호출한 후속 판단은 16/16 성공, 동일 Jev 실제 버전, 입력 36,188→34,604토큰 및 비용 $0.001519896→$0.001453368로 4.377% 감소했다. 판단 지연은 p50 252→336ms, p95 358→567ms로 늘었다. 입력 절감과 지연 개선은 같은 의미가 아니다. 증거: `scratch/jev/improve-appraisal-after.json`.
|
||
|
||
후속 판단 144개 축에 기존 정책을 적용하면 50개가 보류된다. 새 정책에서는 같은 결과 중 42개가 잠정 전이 대상이 되고 8개는 보류된다(높은 confidence 94개). 이 비교는 전이 정책의 차이를 검증하며 감정 정확도 증거가 아니다.
|
||
|
||
대화는 각 버전에서 기존/Jev 각각 3턴×2회, 총 12턴씩 수집했다. 같은 fixture·측정 스크립트·gateway 지문과 생성 설정을 확인했다. model/effort는 기본 설정을 유지했으며 gateway의 모델 이름은 upstream 버전의 독립 검증으로 취급하지 않는다. 중간 실측에서 실제 발화에 없던 이름을 언급한 1건을 발견해 L4에 없는 말·이름·사건을 덧붙이지 않는 지시를 추가했다. 최종 12턴에서 그 오류는 재현되지 않았지만, 두 번의 합성 대화로 환각 방지를 보장하지 않는다. 잘못된 여동생 회상 유도는 변경 전후 모두 받아들이지 않았다.
|
||
|
||
| 전체 응답시간 | 변경 전 Jev | 최종 Jev | 최종 기존 경로 |
|
||
|---|---:|---:|---:|
|
||
| 첫 턴 p50 / p95 (각 2개) | 6,584.8 / 6,906.6ms | 5,978.4 / 6,463.1ms | 5,434.4 / 5,826.3ms |
|
||
| 이후 턴 p50 / p95 (각 4개) | 3,091.5 / 4,278.6ms | 2,537.8 / 2,860.9ms | 2,013.8 / 2,597.2ms |
|
||
|
||
첫 토큰은 전체 출력 검사 때문에 전체 완료 시각과 거의 같다. 전후 수치는 감소했지만 적은 표본·실행 시점·생성문 차이가 있어 코드 변경의 인과 효과나 일반적 속도 우위를 주장하지 않는다. 최종 동시기 비교에서도 Jev 경로는 기존 경로보다 느리다. 원문과 지문은 `scratch/jev/improve-before.json`, `improve-after.json`(중간 결함 포함), `improve-final.json`에 보존했다. 마지막 보고서 12/12 완료와 모든 합성 세션 정리를 확인했으며, 검증용 gateway만 종료했다.
|
||
|
||
신규 artifact 보존 갱신 뒤 전체 API 회귀는 1,172 passed·1 skipped·0 failed다. gateway 82 passed와 web typecheck·build·API 타입 동기화도 통과했고, 마지막 사실 지시 보강 후 감정/마스킹/페르소나 48개와 현재 작업트리 위기 기술 사례 6/6을 재검증해 통과했다. 이전 `matches_head` 미커밋 지문 불일치는 역사 기록으로 보존하되 현재 게이트로 두지 않는다. 코드 SHA `969d9e2c3c94b4f3356defbf02bc863e1c8e30e1`와 API 이미지 `sha256:ba4a6ed98262ead78147e81cf54777f1bd655cf9aeb4f645d73624580b38b516`를 NAS에 적용했고, source hash 7개와 Git 후보가 일치하며 DB·업로드 보존 검사를 통과했다. `production-dialogue-final-20260922.json`의 3턴 합성 probe는 모두 done이고 Jev 판단 지연은 612/245/271ms였다. **판정: 구현·계약·운영 API 배포, 합성·인증 브라우저 runtime과 DB 감정 영속화 검증 수용. 일반적 속도 우위·사용자 체감 지연 개선·한국어 감정 정확도·품질 승격은 미완료.**
|
||
|
||
`USER-LATENCY`는 JEV-001 하위 열린 항목이다. CUA click→reply 도구 관측상한은 T1 7392ms와 T2 13202ms였으나 도구 호출 사이 공백을 포함하므로 정확한 지연이 아니며, T3 UI 관측값은 5001ms였다. audit은 client 3983/3387/3694ms와 Jev 498/316/318ms를 기록했고, 기존 P1 privacy 원문 미조회 비교는 client 3239ms·Jev 470ms였다. 현재는 전체 출력 검사 뒤 글을 표시하고, DB 저장 완료 뒤 음성 요청→서버 전량 수신→브라우저 전량 decode 뒤 재생한다. 첫 발화의 캐시 원인은 단정하지 않는다. 이 표본은 성능 통계나 기준 모델 대비 우위가 아니며 사용자 체감 속도가 해결됐거나 빨라졌다고 주장하지 않는다. 실제 경로의 text display·voice onset·완료 준비를 분리 측정하고 안전 검사를 유지한 최적화로 닫는다.
|
||
|
||
운영 API 기동 로그에는 기존 G6 background 연구 producer의 `SupervisionResearchConflictError` 1건이 관측됐다. producer는 cohort별 예외를 격리해 계속 실행했고 Jev·세션 경로와 분리되며, 이번 배포 diff에는 해당 producer·store 변경이 없다. 따라서 이번 Jev 배포 회귀로 단정하지 않지만 전체 운영 기능 정상 주장을 하지 않는다. 열린 항목 `G6-PRODUCER-CONFLICT`는 기존 submission id와 content hash를 읽기 전용으로 대조하고, 같은 id 재실행의 idempotency 충돌을 해소한 뒤 해당 테스트와 실제 cycle 성공으로 닫는다.
|
||
|
||
### 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 증거 경계를 구분한다.
|